문자열을 줄로 나누는 가장 좋은 방법


143

여러 줄로 된 줄을 어떻게 줄로 나눕니 까?

나도 알아

var result = input.Split("\n\r".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

조금 못 생겼고 빈 줄을 잃습니다. 더 나은 해결책이 있습니까?



1
나는이 솔루션을 좋아하는데, 어떻게 쉽게 만드는지 모르겠다. 두 번째 매개 변수는 물론 비어 있습니다.
NappingRabbit

답변:


172
  • 보기 흉한 경우 불필요한 ToCharArray통화를 제거하십시오 .

  • \n또는 로 나누려면 \r두 가지 옵션이 있습니다.

    • 배열 리터럴을 사용하십시오. 그러나 이것은 Windows 스타일 줄 끝을위한 빈 줄을 제공합니다 \r\n.

      var result = text.Split(new [] { '\r', '\n' });
    • Bart가 나타내는 정규식을 사용하십시오.

      var result = Regex.Split(text, "\r\n|\r|\n");
  • 빈 줄을 유지하려면 왜 C #에 명시 적으로 버리라고 지시합니까? ( StringSplitOptionsparameter) – StringSplitOptions.None대신 사용하십시오.


2
ToCharArray이 ( '\ n'은 줄 바꿈이 될 수 있습니다) 코드 플랫폼 별을 만들 것이다 제거
콘스탄틴 Spirin

1
@Will :에 당신은 콘스탄틴 대신 나에게 언급 된 것을 기회 오프 : 나는 (생각 강력하게 코드를 구문 분석 그것은 또한에 인코딩 된 텍스트 파일을 읽어야합니다 (모든 플랫폼에서 작동하도록, 즉 노력해야) 다른 실행중인 플랫폼에 비해 플랫폼 ). 파싱의 경우, Environment.NewLine내가 생각하는 한 계속 진행됩니다. 실제로 가능한 모든 솔루션 중에서 정규식을 사용하는 솔루션을 선호합니다. 왜냐하면 모든 소스 플랫폼을 올바르게 처리하기 때문입니다.
Konrad Rudolph

2
@Hamish 그냥 열거 형 문서를 보거나 원래 질문을보십시오! 그것은이다 StringSplitOptions.RemoveEmptyEntries.
Konrad Rudolph

8
'\ r \ n \ r \ n'을 포함하는 텍스트는 어떻습니까? string.Split은 4 개의 빈 줄을 반환하지만 '\ r \ n'을 사용하면 2가되어야합니다. '\ r \ n'과 '\ r'이 하나의 파일에 혼합되면 더 나빠집니다.
사용자 이름

1
@SurikovPavel 정규식을 사용하십시오. 그것은 줄 끝의 모든 조합에서 올바르게 작동하기 때문에 분명히 선호되는 변형입니다.
Konrad Rudolph

134
using (StringReader sr = new StringReader(text)) {
    string line;
    while ((line = sr.ReadLine()) != null) {
        // do something
    }
}

12
이것은 내 주관적인 견해에서 가장 깨끗한 접근법입니다.
primo

5
성능 측면에서 string.Split또는에 비해 어떤 아이디어가 Regex.Split있습니까?
Uwe Keim

52

업데이트 : 대체 / 비동기 솔루션 은 여기 를 참조 하십시오 .


이것은 훌륭하게 작동하며 Regex보다 빠릅니다.

input.Split(new[] {"\r\n", "\r", "\n"}, StringSplitOptions.None)

"\r\n"한 줄 바꿈으로 사용되도록 배열에서 첫 번째 를 갖는 것이 중요합니다 . 위의 Regex 솔루션 중 하나와 동일한 결과를 제공합니다.

Regex.Split(input, "\r\n|\r|\n")

Regex.Split(input, "\r?\n|\r")

Regex가 약 10 배 느리다는 것을 제외하고. 내 테스트는 다음과 같습니다.

Action<Action> measure = (Action func) => {
    var start = DateTime.Now;
    for (int i = 0; i < 100000; i++) {
        func();
    }
    var duration = DateTime.Now - start;
    Console.WriteLine(duration);
};

var input = "";
for (int i = 0; i < 100; i++)
{
    input += "1 \r2\r\n3\n4\n\r5 \r\n\r\n 6\r7\r 8\r\n";
}

measure(() =>
    input.Split(new[] {"\r\n", "\r", "\n"}, StringSplitOptions.None)
);

measure(() =>
    Regex.Split(input, "\r\n|\r|\n")
);

measure(() =>
    Regex.Split(input, "\r?\n|\r")
);

산출:

00 : 00 : 03.8527616

00 : 00 : 31.8017726

00 : 00 : 32.5557128

확장 방법은 다음 과 같습니다 .

public static class StringExtensionMethods
{
    public static IEnumerable<string> GetLines(this string str, bool removeEmptyLines = false)
    {
        return str.Split(new[] { "\r\n", "\r", "\n" },
            removeEmptyLines ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None);
    }
}

용법:

input.GetLines()      // keeps empty lines

input.GetLines(true)  // removes empty lines

독자에게 답변을 더 유용하게하려면 더 자세한 내용을 추가하십시오.
Mohit Jain

끝난. 또한 Regex 솔루션과 성능을 비교하는 테스트를 추가했습니다.
orad

때문에 이하와 같은 기능으로 되돌아 다소 빠른 패턴을 사용하는 경우 일[\r\n]{1,2}
ΩmegaMan

@OmegaMan 다른 동작이 있습니다. 일치하지 \n\r않거나 \n\n한 줄 바꿈으로 잘못되었습니다.
orad

3
@OmegaMan Hello\n\nworld\n\n에지 케이스는 어떻습니까? 텍스트가있는 한 줄, 빈 줄, 텍스트가있는 다른 줄, 빈 줄이 있습니다.
Brandin

36

Regex.Split을 사용할 수 있습니다.

string[] tokens = Regex.Split(input, @"\r?\n|\r");

편집 : |\r(이전) Mac 라인 터미네이터를 설명하기 위해 추가되었습니다 .


OS X 스타일의 텍스트 파일 \r에서는 줄 끝으로 만 사용되므로 작동하지 않습니다 .
Konrad Rudolph 08 년

2
@Konrad Rudolph : AFAIK, '\ r'은 매우 오래된 MacOS 시스템에서 사용되었으며 더 이상 거의 만나지 않습니다. 그러나 (내가 잘못 해요 경우 또는) 그것에 대한 계정에 OP 요구, 다음 정규식 쉽게 물론 그것을 위해 계정에 확장 될 수있는 경우 : \ 연구를 \ n | \ 연구?
바트 Kiers

@Bart : 나는 당신에게있는 거 잘못을 생각하지 않는다 그러나 나는 반복 프로그래머로서 내 경력에서 가능한 모든 라인 엔딩가 발생했습니다.
Konrad Rudolph

@ Konrad, 당신 말이 맞을 것입니다. 미안보다 안전합니다.
Bart Kiers

1
@ ΩmegaMan : 빈 줄이 없어집니다 (예 : \ n \ n).
Mike Rosoft

9

빈 줄을 유지하려면 StringSplitOptions를 제거하십시오.

var result = input.Split(System.Environment.NewLine.ToCharArray());

2
NewLine은 '\ n'일 수 있으며 입력 텍스트에는 "\ n \ r"이 포함될 수 있습니다.
Konstantin Spirin

4

나는이 있었다 다른 대답 잭의 기반으로하지만,이 한 대답을 , 훨씬 빠르게되고 는 비동기 적으로 작동하기 때문에 비록 약간 느린 선호 될 수 있습니다.

public static class StringExtensionMethods
{
    public static IEnumerable<string> GetLines(this string str, bool removeEmptyLines = false)
    {
        using (var sr = new StringReader(str))
        {
            string line;
            while ((line = sr.ReadLine()) != null)
            {
                if (removeEmptyLines && String.IsNullOrWhiteSpace(line))
                {
                    continue;
                }
                yield return line;
            }
        }
    }
}

용법:

input.GetLines()      // keeps empty lines

input.GetLines(true)  // removes empty lines

테스트:

Action<Action> measure = (Action func) =>
{
    var start = DateTime.Now;
    for (int i = 0; i < 100000; i++)
    {
        func();
    }
    var duration = DateTime.Now - start;
    Console.WriteLine(duration);
};

var input = "";
for (int i = 0; i < 100; i++)
{
    input += "1 \r2\r\n3\n4\n\r5 \r\n\r\n 6\r7\r 8\r\n";
}

measure(() =>
    input.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None)
);

measure(() =>
    input.GetLines()
);

measure(() =>
    input.GetLines().ToList()
);

산출:

00 : 00 : 03.9603894

00 : 00 : 00.0029996

00 : 00 : 04.8221971


실제로 열거 자의 결과를 검사하지 않기 때문에 이것이 실행되지 않는지 궁금합니다. 불행히도, 확인하기에는 너무 게으르다.
제임스 홀웰

예, 사실입니다! 두 호출 모두에 .ToList ()를 추가하면 실제로 StringReader 솔루션이 느려집니다! 내 컴퓨터에 그것은 6.74s 대 5.10s이다
JCH2k

말이 되네요 줄을 비동기식으로 가져올 수 있기 때문에 여전히이 방법을 선호합니다.
orad

어쩌면 당신은 당신의 다른 답변에서 "더 나은 솔루션"헤더를 제거하고 이것을 편집해야합니다 ...
JCH2k

4
string[] lines = input.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

2

약간 비틀었지만 반복 블록은 다음과 같습니다.

public static IEnumerable<string> Lines(this string Text)
{
    int cIndex = 0;
    int nIndex;
    while ((nIndex = Text.IndexOf(Environment.NewLine, cIndex + 1)) != -1)
    {
        int sIndex = (cIndex == 0 ? 0 : cIndex + 1);
        yield return Text.Substring(sIndex, nIndex - sIndex);
        cIndex = nIndex;
    }
    yield return Text.Substring(cIndex + 1);
}

그런 다음 전화를 걸 수 있습니다.

var result = input.Lines().ToArray();

1
    private string[] GetLines(string text)
    {

        List<string> lines = new List<string>();
        using (MemoryStream ms = new MemoryStream())
        {
            StreamWriter sw = new StreamWriter(ms);
            sw.Write(text);
            sw.Flush();

            ms.Position = 0;

            string line;

            using (StreamReader sr = new StreamReader(ms))
            {
                while ((line = sr.ReadLine()) != null)
                {
                    lines.Add(line);
                }
            }
            sw.Close();
        }



        return lines.ToArray();
    }

1

혼합 줄 끝을 올바르게 처리하는 것은 까다 롭습니다 . 우리가 알다시피, 라인 종료 문자가 "줄 바꿈"할 수있다 (ASCII 10, \n, \x0A, \u000A), "캐리지 리턴"(ASCII 13, \r, \x0D, \u000D), 또는 이들의 조합. DOS로 돌아 가면 Windows는 두 문자 시퀀스 CR-LF를 사용하므로이 \u000D\u000A조합은 한 줄만 방출해야합니다. 유닉스는 단일을 사용 \u000A하고 아주 오래된 맥은 단일 \u000D문자를 사용했습니다 . 단일 텍스트 파일 내에서 이러한 문자의 임의 혼합을 처리하는 표준 방법은 다음과 같습니다.

  • 각각의 모든 CR 또는 LF 문자는 다음 행을 제외 하고 다음 줄로 건너 뛰어야합니다 .
  • ... CR LF 바로 뒤에있는 경우 ( \u000D\u000A)는 다음 두 가지가 함께 하나의 광고를 건너.
  • String.Empty 행을 리턴하지 않는 유일한 입력입니다 (모든 문자는 하나 이상의 행을 수반 함)
  • 마지막 줄은 CR이나 LF가 없어도 반환해야합니다.

위의 규칙은 StringReader.ReadLine 의 동작 및 관련 함수를 설명하며 아래에 표시된 함수는 동일한 결과를 생성합니다. CR / LF의 임의의 시퀀스 또는 조합을 올바르게 처리하기 위해 이러한 지침을 충실하게 구현 하는 효율적인 C # 줄 바꿈 기능입니다. 열거 된 줄에는 CR / LF 문자가 포함되지 않습니다. 빈 라인은 보존로 반환됩니다 String.Empty.

/// <summary>
/// Enumerates the text lines from the string.
///   ⁃ Mixed CR-LF scenarios are handled correctly
///   ⁃ String.Empty is returned for each empty line
///   ⁃ No returned string ever contains CR or LF
/// </summary>
public static IEnumerable<String> Lines(this String s)
{
    int j = 0, c, i;
    char ch;
    if ((c = s.Length) > 0)
        do
        {
            for (i = j; (ch = s[j]) != '\r' && ch != '\n' && ++j < c;)
                ;

            yield return s.Substring(i, j - i);
        }
        while (++j < c && (ch != '\r' || s[j] != '\n' || ++j < c));
}

참고 : StringReader각 호출에서 인스턴스를 만드는 오버 헤드가 마음에 들지 않으면 다음 C # 7 코드를 대신 사용할 수 있습니다 . 언급 한 바와 같이, 위의 예는 약간 더 효율적일 수 있지만,이 두 함수는 모두 동일한 결과를 생성합니다.

public static IEnumerable<String> Lines(this String s)
{
    using (var tr = new StringReader(s))
        while (tr.ReadLine() is String L)
            yield return L;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.