如何将多行字符串拆分成行?
我知道下面这种方法。
var result = input.Split("\n\r".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
看起来有点丑,并且会删除空行。有更好的解决方案吗?
如何将多行字符串拆分成行?
我知道下面这种方法。
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
代替。
Environment.NewLine
对我来说是不可行的。事实上,对于所有可能的解决方案,我更喜欢使用正则表达式,因为只有这种方法可以正确处理所有源平台。 - Konrad RudolphStringSplitOptions
,并且使用了枚举常量 RemoveEmptyEntries
。你需要保持原文意思的基础上进行翻译,尽可能地让翻译更加易懂。 - Konrad Rudolphusing (StringReader sr = new StringReader(text)) {
string line;
while ((line = sr.ReadLine()) != null) {
// do something
}
}
string.Split
或 Regex.Split
,有没有任何关于性能方面的想法? - Uwe Keim"example"
和"example\r\n"
将只产生一行,而"example\r\n\r\n"
将产生两行。这种行为在这里讨论:https://github.com/dotnet/runtime/issues/27715 - Alielson Piffer这段代码效果很好,比正则表达式更快:
input.Split(new[] {"\r\n", "\r", "\n"}, StringSplitOptions.None)
"\r\n"
以使其被作为一行换行符。以上代码给出了与以下任何一个正则表达式解决方案相同的结果:Regex.Split(input, "\r\n|\r|\n")
Regex.Split(input, "\r?\n|\r")
除了正则表达式,它的速度大约慢了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
[\r\n]{1,2}
,由于减少了回溯,模式会变得更快一些,但功能仍然相同。 - ΩmegaMan\n\r
或\n\n
匹配为单个换行符,这是不正确的。 - oradstring[] tokens = Regex.Split(input, @"\r?\n|\r");
编辑:添加 |\r
以考虑(旧版)Mac的行终止符。
\r
作为行结尾。 - Konrad Rudolph[\r\n]{1,2}
可以减少回溯并保持相同的功能。 - ΩmegaManvar result = input.Split(System.Environment.NewLine.ToCharArray());
string[] lines = input.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
我之前有这个其他答案,但是基于Jack的答案,虽然略慢一些但它可以异步工作,因此可能更受欢迎。
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()
);
Output:
00:00:03.9603894
00:00:00.0029996
00:00:04.8221971
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;
}
}
}
IEnumerable<>
吗? - Konstantin Spirin虽然晚了一步,但我一直在使用一个简单的扩展方法集来实现这个,它利用了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);
略微有些复杂,但可以使用迭代器块来实现:
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();