我有一段文本,我想在不丢失结尾的 \r 和 \n 的情况下获得它的行。现在,我有以下(次优代码):
string[] lines = tbIn.Text.Split('\n')
.Select(t => t.Replace("\r", "\r\n")).ToArray();
所以我想知道 - 有没有更好的方法呢?
接受的答案
string[] lines = Regex.Split(tbIn.Text, @"(?<=\r\n)(?!$)");
以下似乎可以完成这项工作:
string[] lines = Regex.Split(tbIn.Text, @"(?<=\r\n)(?!$)");
(?<=\r\n) 在 \r\n 之后使用“正向后视”来匹配而不消耗它。
(?!$) 使用负前瞻来防止在输入末尾匹配,从而避免最后一行只是一个空字符串。
如果您只是要替换换行符 ( \n),请执行以下操作:
string[] lines = tbIn.Text.Split('\n')
.Select(t => t + "\r\n").ToArray();
string[] lines = Regex.Split(tbIn.Text, "\r\n")
.Select(t => t + "\r\n").ToArray();
类似于使用此正则表达式的内容: [^\n\r]*\r\n
然后使用 Regex.Matches()。问题是您需要 Group(1) 从每个匹配项中创建您的字符串列表。在 Python 中,您只需使用 map() 函数。不确定在 .NET 中执行此操作的最佳方法,您可以从那里获取 ;-)
Dmitri,您的解决方案实际上非常简洁明了。唯一更有效的方法是将字符串拆分字符保留在生成的数组中,但 API 根本不允许这样做。因此,每个解决方案都需要遍历数组并执行某种修改(这在 C# 中意味着每次都分配新字符串)。我认为你能希望的最好的就是不要重新创建数组:
string[] lines = tbIn.Text.Split('\n');
for (int i = 0; i < lines.Length; ++i)
{
lines[i] = lines[i].Replace("\r", "\r\n");
}
...但正如您所见,这看起来更麻烦!如果性能很重要,这可能会好一些。如果真的很重要,您应该考虑使用 IndexOf() 手动解析字符串,一次找到 '\r 的一个,然后自己创建数组。不过,这要多得多的代码,而且可能没有必要。
您的解决方案和这个解决方案的副作用之一是,如果 TextBox 中还没有最后一行,您将不会在最后一行获得终止的“\r\n”。这是你所期望的吗?空白行呢……您希望它们出现在“行”中吗?
您可以使用正则表达式来实现这一点。这是一个扩展方法:
public static string[] SplitAndKeepDelimiter(this string input, string delimiter)
{
MatchCollection matches = Regex.Matches(input, @"[^" + delimiter + "]+(" + delimiter + "|$)", RegexOptions.Multiline);
string[] result = new string[matches.Count];
for (int i = 0; i < matches.Count ; i++)
{
result[i] = matches[i].Value;
}
return result;
}
我不确定这是否是一个更好的解决方案。你的非常紧凑和简单。
与往常一样,扩展方法好东西:)
public static class StringExtensions
{
public static IEnumerable<string> SplitAndKeep(this string s, string seperator)
{
string[] obj = s.Split(new string[] { seperator }, StringSplitOptions.None);
for (int i = 0; i < obj.Length; i++)
{
string result = i == obj.Length - 1 ? obj[i] : obj[i] + seperator;
yield return result;
}
}
}
用法:
string text = "One,Two,Three,Four";
foreach (var s in text.SplitAndKeep(","))
{
Console.WriteLine(s);
}
输出:
一,
二,
三,
四