programing

문자열을 줄로 분할하는 가장 좋은 방법

newsource 2023. 8. 13. 09:46

문자열을 줄로 분할하는 가장 좋은 방법

다중 줄 문자열을 줄로 분할하는 방법은 무엇입니까?

나는 이 방법을 알고 있습니다.

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

약간 못생기고 빈 줄이 없어요.더 좋은 해결책이 있습니까?

  • 보기 흉하면 불필요한 것을 제거합니다.ToCharArray불러.

  • 다음 중 하나로 분할하려면\n또는\r두 가지 옵션이 있습니다.

    • 배열 리터럴을 사용합니다. 하지만 이렇게 하면 Windows 스타일의 줄 끝에 빈 줄이 표시됩니다.\r\n:

      var result = text.Split(new [] { '\r', '\n' });
      
    • Bart에 표시된 대로 정규식을 사용합니다.

      var result = Regex.Split(text, "\r\n|\r|\n");
      
  • 만약 당신이 빈 줄을 보존하고 싶다면, 왜 당신은 C#에게 그것들을 버리라고 명시적으로 말하는가? (StringSplitOptions매개 변수) – 사용StringSplitOptions.None대신.

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

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


이것은 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

Regex를 사용할 수 있습니다.분할:

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

편집: 추가됨|\r(이전의) Mac 라인 터미네이터를 설명합니다.

빈 줄을 유지하려면 StringSplitOptions(StringSplit옵션)를 제거하면 됩니다.

var result = input.Split(System.Environment.NewLine.ToCharArray());
string[] lines = input.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

저는 다른 답을 가지고 있었지만, 대답에 따르면, 이것은 약간 느리긴 하지만 비동기식으로 작동하기 때문에 훨씬 더 빠를 수 있습니다.

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

약간 비틀리지만 반복기 블록을 사용합니다.

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();
    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();
    }

혼합된 줄 끝을 제대로 처리하기가 어렵습니다.아시다시피, 회선 종료 문자는 "Line Feed"(ASCII 10,\n,\x0A,\u000A), "캐리지 리턴"(ASCII 13,\r,\x0D,\u000D) 또는 이들의 조합.DOS로 돌아가서 Windows는 CR-LF라는 두 문자 시퀀스를 사용합니다.\u000D\u000A따라서 이 조합은 한 줄만 방출해야 합니다.Unix는 단일 제품을 사용합니다.\u000A그리고 아주 오래된 Mac은 싱글을 사용했습니다.\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;
}

할당 없이 문자열을 여러 줄로 분할합니다.

public static LineEnumerator GetLines(this string text) {
    return new LineEnumerator( text.AsSpan() );
}

internal ref struct LineEnumerator {

    private ReadOnlySpan<char> Text { get; set; }
    public ReadOnlySpan<char> Current { get; private set; }

    public LineEnumerator(ReadOnlySpan<char> text) {
        Text = text;
        Current = default;
    }

    public LineEnumerator GetEnumerator() {
        return this;
    }

    public bool MoveNext() {
        if (Text.IsEmpty) return false;

        var index = Text.IndexOf( '\n' ); // \r\n or \n
        if (index != -1) {
            Current = Text.Slice( 0, index + 1 );
            Text = Text.Slice( index + 1 );
            return true;
        } else {
            Current = Text;
            Text = ReadOnlySpan<char>.Empty;
            return true;
        }
    }


}

파티에 늦었지만, 나는 단지 그것을 위해 간단한 확장 방법 모음을 사용해왔고, 그것은 활용합니다.TextReader.ReadLine():

public static class StringReadLinesExtension
{
    public static IEnumerable<string> GetLines(this string text) => GetLines(new StringReader(text));
    public static IEnumerable<string> GetLines(this Stream stm) => GetLines(new StreamReader(stm));
    public static IEnumerable<string> GetLines(this TextReader reader) {
        string line;
        while ((line = reader.ReadLine()) != null)
            yield return line;
        reader.Dispose();
        yield break;
    }
}

코드를 사용하는 것은 정말 사소한 일입니다.

// If you have the text as a string...
var text = "Line 1\r\nLine 2\r\nLine 3";
foreach (var line in text.GetLines())
    Console.WriteLine(line);
// You can also use streams like
var fileStm = File.OpenRead("c:\tests\file.txt");
foreach(var line in fileStm.GetLines())
    Console.WriteLine(line);

이것이 누군가에게 도움이 되길 바랍니다.

언급URL : https://stackoverflow.com/questions/1508203/best-way-to-split-string-into-lines