代码之家  ›  专栏  ›  技术社区  ›  Konstantin Spirin

将字符串拆分为行的最佳方法

  •  114
  • Konstantin Spirin  · 技术社区  · 17 年前

    如何将多行字符串拆分为多行?

    我知道这条路

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

    10 回复  |  直到 13 年前
        1
  •  201
  •   Konrad Rudolph    8 年前
    • 如果它看起来很难看,就去掉不必要的部分 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 相反

        2
  •  143
  •   Colonel Panic    10 年前
    using (StringReader sr = new StringReader(text)) {
        string line;
        while ((line = sr.ReadLine()) != null) {
            // do something
        }
    }
    
        3
  •  69
  •   Uwe Keim    7 年前

    更新:见 here


    这非常有效,比正则表达式更快:

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

    重要的是要有 "\r\n" 数组中的第一个,因此它被视为一个换行符。以上给出的结果与以下任一正则表达式解决方案相同:

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

    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
    
        4
  •  36
  •   Wolf    9 年前

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

    |\r 说明(旧的)Mac线终端。

        5
  •  10
  •   Jonas Elfström    17 年前

    如果要保留空行,只需删除StringSplitOptions。

    var result = input.Split(System.Environment.NewLine.ToCharArray());
    
        6
  •  7
  •   Simon Mattes    6 年前
    string[] lines = input.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
    
        7
  •  5
  •   orad    8 年前

    other answer 但这一次,基于杰克的 answer , 速度要快得多 可能是首选,因为它是异步工作的,尽管速度稍慢。

    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

        8
  •  2
  •   JDunkerley    17 年前

    稍微扭曲,但需要一个迭代器块:

    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();
    
        9
  •  2
  •   Glenn Slayden    7 年前

    这很难处理 混合的 \n , \x0A , \u000A ),“回车”(ASCII 13, \r , \x0D , \u000D ),或两者的某种组合。回到DOS,Windows使用两个字符序列CR-LF \u000D\u000A ,因此此组合应仅发射一条线。Unix使用单个 ,非常旧的Mac电脑使用一个 \u000D

    • 每个CR或LF字符都应跳到下一行 除了
    • \u000D\u000A )然后这两个 在一起 只跳过一行。
    • String.Empty 是唯一不返回任何行的输入(任何字符至少包含一行)
    • 最后一行必须返回,即使它既没有CR也没有LF。

    前面的规则描述了 StringReader.ReadLine C# .

    /// <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;
    }
    
        10
  •  1
  •   John Thompson    11 年前
        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();
        }
    
        11
  •  1
  •   Denis535    5 年前

    将字符串拆分为行而不进行任何分配。

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