在.NET中在换行符上拆分字符串的最简单方法?


806

我需要在.NET中将字符串拆分为换行符,而我知道的唯一拆分字符串的方法是使用Split方法。但是,这将不允许我(轻松)在换行符上进行拆分,那么最好的方法是什么?


2
为什么不呢?只是各执System.Environment.NewLine
aviraldg

16
但是,您必须将其包装在string []中并添加一个额外的参数,然后……感觉有点笨拙。
RCIX

Answers:


1411

要分割字符串,您需要使用采用字符串数组的重载:

string[] lines = theText.Split(
    new[] { Environment.NewLine },
    StringSplitOptions.None
);

编辑:
如果要处理文本中不同类型的换行符,则可以使用该功能来匹配多个字符串。这将在两种类型的换行符上正确分割,并保留文本中的空行和空格:

string[] lines = theText.Split(
    new[] { "\r\n", "\r", "\n" },
    StringSplitOptions.None
);

3
@RCIX:向该方法发送正确的参数有点尴尬,因为您将其用于比其功能简单得多的事情。至少它在那里,在框架2之前,您必须使用正则表达式或构建自己的拆分例程以对字符串进行拆分...
Guffa

4
@Leandro:该Environment.NewLine属性包含系统的默认换行符。以Windows系统为例"\r\n"
Guffa 2012年

3
@Leandro:一种猜测是程序会在每行的末尾\n留下a的情况\r下拆分,然后在两行\r\n之间输出a 。
Guffa 2012年

3
@Samuel:\r和换\n码序列(以及其他)对C#编译器有特殊的含义。VB没有这些转义序列,因此使用了这些常量。
Guffa

2
如果要接受来自各种操作系统的文件,则还可以在定界符列表的开头添加“ \ n \ r”,在结尾添加“ \ r”。我不确定是否值得提高性能。(en.wikipedia.org/wiki/Newline
user420667

121

怎么样使用StringReader

using (System.IO.StringReader reader = new System.IO.StringReader(input)) {
    string line = reader.ReadLine();
}

13
这是我最喜欢的。我包装了一个扩展方法,并返回了当前行:gist.github.com/ronnieoverby/7916886
Ronnie Overby

3
这是我为.netcf 3.5找到的唯一非正则表达式解决方案
Carl

8
当输入很大并且将其全部复制到阵列变得缓慢/占用大量内存时,特别好。
Alejandro 2014年

1
如所写,此答案仅读取第一行。有关应添加到此答案中的循环,请参见Steve Cooper的while答案。
ToolmakerSteve

48

您应该能够轻松拆分字符串,如下所示:

aString.Split(Environment.NewLine.ToCharArray());

46
在非* nix系统上,它将在换行符字符串中的单独字符(即CR和LF字符)上分割。这将导致每行之间多余的空字符串。
加法

如果我错了,请纠正我,但是不会在字符\和n上分开吗?
RCIX

7
@RCIX:不,\ r和\ n代码代表单个字符。字符串“ \ r \ n”是两个字符,而不是四个。
加法

10
如果添加参数StringSplitOptions.RemoveEmptyEntries,则此方法将完美运行。
鲁宾

18
@Ruben:不,不会。Serge已经在他的回答中提出了建议,并且我已经aldready解释说,它还将删除原始文本中应保留的空白行。
加法

34

尽量避免使用string.Split作为一般解决方案,因为在使用该函数的任何地方都会使用更多的内存-原始字符串和拆分副本都在内存中。相信我,当您开始扩展时,这可能是个棘手的问题-运行一个处理100MB文档的32位批处理应用程序,您将遇到8个并发线程。不是说我去过那里...

相反,使用这样的迭代器;

    public static IEnumerable<string> SplitToLines(this string input)
    {
        if (input == null)
        {
            yield break;
        }

        using (System.IO.StringReader reader = new System.IO.StringReader(input))
        {
            string line;
            while( (line = reader.ReadLine()) != null)
            {
                yield return line;
            }
        }
    }

这将使您可以在数据周围进行更有效的内存循环。

foreach(var line in document.SplitToLines()) 
{
    // one line at a time...
}

当然,如果您希望将其全部存储在内存中,则可以执行此操作。

var allTheLines = document.SplitToLines.ToArray();

我去过那里(解析大型HTML文件并用完内存)。是的,避免使用string.Split。使用string.Split可能会导致使用大对象堆(LOH)-但我对此不是100%肯定的。
彼得·莫滕森

如果您将SplitToLines设为静态方法(似乎是dd),那么如何执行blah.SplitToLines.. 例如document.SplitToLines...
barlop

嗯,我看到您this输入了形式参数,使其成为扩展方法。
barlop

26

根据Guffa的答案,在扩展类中,使用:

public static string[] Lines(this string source) {
    return source.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
}

9

对于字符串变量s

s.Split(new string[]{Environment.NewLine},StringSplitOptions.None)

这将使用您环境的行尾定义。在Windows上,行尾为CR-LF(回车,换行)或C#的转义字符\r\n

这是一个可靠的解决方案,因为如果您将的行与合并String.Join,则等于原始字符串:

var lines = s.Split(new string[]{Environment.NewLine},StringSplitOptions.None);
var reconstituted = String.Join(Environment.NewLine,lines);
Debug.Assert(s==reconstituted);

不该做什么:

  • 使用StringSplitOptions.RemoveEmptyEntries,因为这会破坏标记(例如Markdown),其中空行具有语法目的。
  • 在split上拆分new char[]{Environment.NewLine},因为在Windows上,这将为每个新行创建一个空字符串元素。

基本上,这里的答案与最受好评的答案相同,但是它确实具有不错的单元测试和警告。
vapcguy

8

正则表达式也是一种选择:

    private string[] SplitStringByLineFeed(string inpString)
    {
        string[] locResult = Regex.Split(inpString, "[\r\n]+");
        return locResult;
    }

7
如果您想精确匹配行,并保留空白行,则此regex字符串会更好:"\r?\n"
罗里·奥凯恩

7

我只是以为我要加上两位,因为这个问题的其他解决方案不属于可重用的代码分类,而且也不方便。

以下代码块扩展了string对象,以便在使用字符串时可以将其作为自然方法使用。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.Collections.ObjectModel;

namespace System
{
    public static class StringExtensions
    {
        public static string[] Split(this string s, string delimiter, StringSplitOptions options = StringSplitOptions.None)
        {
            return s.Split(new string[] { delimiter }, options);
        }
    }
}

现在,您可以.Split()从任何字符串使用函数,如下所示:

string[] result;

// Pass a string, and the delimiter
result = string.Split("My simple string", " ");

// Split an existing string by delimiter only
string foo = "my - string - i - want - split";
result = foo.Split("-");

// You can even pass the split options parameter. When omitted it is
// set to StringSplitOptions.None
result = foo.Split("-", StringSplitOptions.RemoveEmptyEntries);

要分割换行符,只需传递 "\n""\r\n"作为定界符参数即可。

注释: 如果Microsoft实现了此重载,那就太好了。


Environment.Newline优先于\n或进行硬编码\r\n
迈克尔·布莱克本

3
@MichaelBlackburn-这是一个无效的语句,因为没有上下文。Environment.Newline用于跨平台兼容性,不适用于使用与当前操作系统不同的线路终端的文件。有关更多信息请参见此处,因此它实际上取决于开发人员正在使用什么工具。使用Environment.Newline确保确保OS之间的行返回类型不一致,其中“硬编码”使开发人员可以完全控制。
Kraang Prime '18 -4-19

2
@MichaelBlackburn-不需要您粗鲁。我只是提供信息。 .Newline这不是魔术,在引擎盖下,它只是上面提供的字符串(基于它是在unix还是在Windows上运行)的切换。最安全的选择是,首先用字符串替换所有“ \ r \ n”,然后在“ \ n”上分割。使用.Newline失败的地方是当您处理由其他程序使用换行符的其他方法保存的文件时。如果您每次都始终使用当前操作系统的换行符来读取文件,则此方法很好用。
Kraang Prime '18 -4-20

因此,我所听到的是最易读的方式(也许会更多地使用内存)foo = foo.Replace("\r\n", "\n"); string[] result = foo.Split('\n');。我是否正确理解这适用于所有平台?
John Doe

4

我目前在VB.NET中使用此功能(基于其他答案):

Private Shared Function SplitLines(text As String) As String()
    Return text.Split({Environment.NewLine, vbCrLf, vbLf}, StringSplitOptions.None)
End Function

它尝试首先在平台本地的换行符上分割,然后回退到每个可能的换行符。

到目前为止,我只需要在一堂课中使用它。如果那改变了,我可能会做这个Public并将其移至实用程序类,甚至可能使其成为扩展方法。

很好地说明了如何加入备份行:

Private Shared Function JoinLines(lines As IEnumerable(Of String)) As String
    Return String.Join(Environment.NewLine, lines)
End Function

@Samuel-注意报价。它们实际上确实具有该含义。"\r"=返回。 "\r\n"=返回+新行。(请查看这篇文章和接受的解决方案在这里
Kraang总理

@Kraang Hmm ..我已经很长时间没有使用.NET了。如果这么多人投票否决了我,我会感到惊讶。我看到我也评论了Guffa的答案,并在那里得到了澄清。我已删除对此答案的评论。感谢您的注意。
塞缪尔

2

好吧,实际上拆分应该可以:

//Constructing string...
StringBuilder sb = new StringBuilder();
sb.AppendLine("first line");
sb.AppendLine("second line");
sb.AppendLine("third line");
string s = sb.ToString();
Console.WriteLine(s);

//Splitting multiline string into separate lines
string[] splitted = s.Split(new string[] {System.Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);

// Output (separate lines)
for( int i = 0; i < splitted.Count(); i++ )
{
    Console.WriteLine("{0}: {1}", i, splitted[i]);
}

2
RemoveEmptyEntries选项将从文本中删除空行。在某些情况下这可能是合乎需要的,但普通分割应保留空白行。
加法

是的,您是对的,我只是做出了这样的假设:...好吧,空白行并不有趣;)
MaciekTalaska

1
string[] lines = text.Split(
  Environment.NewLine.ToCharArray(), 
  StringSplitOptions.RemoveEmptyStrings);

RemoveEmptyStrings选项将确保你没有空项是由于\ N按照一个\ r

(编辑以反映评论:)请注意,它还将丢弃文本中的真正空白行。这通常是我想要的,但可能不是您的要求。


RemoveEmptyStrings选项还将删除空行,因此,如果文本中包含空行,它将无法正常工作。
加法

你可能想保留真正的空行:\ r \ n \ r \ n
超薄

0

我不知道Environment.Newline,但是我想这是一个很好的解决方案。

我的尝试是:

        string str = "Test Me\r\nTest Me\nTest Me";
        var splitted = str.Split('\n').Select(s => s.Trim()).ToArray();

附加的.Trim删除可能仍然存在的任何\ r或\ n(例如,在Windows上但使用os x换行符分割字符串时)。可能不是最快的方法。

编辑:

正如注释正确指出的那样,这还会删除行首或换行之前的所有空格。如果需要保留该空格,请使用其他选项之一。


修剪还将删除行首和结尾处的任何空白,例如缩进。
加法

“ .Trim删除可能仍然存在的任何\ r或\ n”-哎呀。为什么不编写健壮的代码呢?
bzlm

也许我弄错了这个问题,但尚不清楚/必须保留空白。当然您是对的,Trim()也会删除空格。
最多

1
@Max:哇,等等,直到我告诉老板允许代码执行规范中未明确排除的任何事情...;)
Guffa

-2

愚蠢的答案:写入临时文件,以便可以使用古老的文件 File.ReadLines

var s = "Hello\r\nWorld";
var path = Path.GetTempFileName();
using (var writer = new StreamWriter(path))
{
    writer.Write(s);
}
var lines = File.ReadLines(path);

1
避免使用var,因为它没有定义变量的类型,因此您可能不了解如何使用该对象或该对象表示什么。另外,这显示了编写各行,甚至没有指定文件名,因此我怀疑它是否可以工作。然后,在读取时,再次未指定文件的路径。假设pathC:\Temp\test.txt,那么您应该拥有string[] lines = File.ReadLines(path);
vapcguy

1
@vapcguy我刚刚读了什么?-我建议您重新阅读该文章或在控制台程序中对其进行调试,因为您所说的一切都是错误的| 路径设置在Path.GetTempFileName | var是C#中的一个常见且推荐的定义-顺便说一句,它确实定义了一个变量的类型......编辑:我并不是说这是一个好的解决方案
koanbock

@koanbock好的,所以我查找了Path.GetTempFileName msdn.microsoft.com/zh-cn/library/…它说它创建了一个零字节的文件并返回“该文件的完整路径”。我可以发誓之前尝试过此方法,但它却出现了异常,因为它没有找到文件,而是返回了文件夹位置。我知道使用using的参数var,但是我不建议您使用它,因为它没有显示变量对象是什么。它混淆了它。
vapcguy

-3
using System.IO;

string textToSplit;

if (textToSplit != null)
{
    List<string> lines = new List<string>();
    using (StringReader reader = new StringReader(textToSplit))
    {
        for (string line = reader.ReadLine(); line != null; line = reader.ReadLine())
        {
            lines.Add(line);
        }
    }
}

-5

实际上很简单。

VB.NET:

Private Function SplitOnNewLine(input as String) As String
    Return input.Split(Environment.NewLine)
End Function

C#:

string splitOnNewLine(string input)
{
    return input.split(environment.newline);
}

4
完全不正确,不起作用。另外,在C#中,Environment.NewLine就像在VB中一样。
vapcguy

请参见VB.NET中的行尾标识符?用于换行的不同选项。
彼得·莫滕森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.