我有一个长度未知的字符串
它的格式为
\nline
\nline
\nline
不知道字符串有多长,我该如何获取最后10行的内容?每行之间用"\n"分隔。
我有一个长度未知的字符串
它的格式为
\nline
\nline
\nline
不知道字符串有多长,我该如何获取最后10行的内容?每行之间用"\n"分隔。
string.Split
的任何方法都是低效的,因为整个字符串都必须被处理。一个高效的解决方案将不得不从字符串的末尾运行。这里提供了一个正则表达式的方法。List<string>
,因为结果需要在返回之前被反转(因此使用了Insert
方法)。private static List<string> TakeLastLines(string text, int count)
{
List<string> lines = new List<string>();
Match match = Regex.Match(text, "^.*$", RegexOptions.Multiline | RegexOptions.RightToLeft);
while (match.Success && lines.Count < count)
{
lines.Insert(0, match.Value);
match = match.NextMatch();
}
return lines;
}
var result = text.Split('\n').Reverse().Take(10).ToArray();
Reverse
。ToArray()
是多余的,因为 OP 没有提到他需要一个数组。 - Tim SchmelterEnumerable.Skip
的方法,后来他删除了他的评论。 - Tim SchmelterSkip
枚举了整个(巨大的)数组...”,我认为如果你真的在谈论Enumerable.Skip
,那么这句话是错误的。 - julealgonEnumerable.Reverse
不同,Enumerable.Skip
没有针对 ICollection<T>
使用 for-loop
进行优化。它会枚举序列。 - Tim SchmelterSplit()
函数可以通过\n
将字符串分割成数组,然后取该数组的最后10个元素。
for(int i=arr.Length-10;i<arr.Length;i++)String line=arr[i];
- Tim Schmelterusing System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string x = "a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\ni\r\nj\r\nk\r\nl\r\nm\r\nn\r\no\r\np";
foreach(var line in x.SplitAsEnumerable("\r\n").TakeLast(10))
Console.WriteLine(line);
Console.ReadKey();
}
}
static class LinqExtensions
{
public static IEnumerable<string> SplitAsEnumerable(this string source)
{
return SplitAsEnumerable(source, ",");
}
public static IEnumerable<string> SplitAsEnumerable(this string source, string seperator)
{
return SplitAsEnumerable(source, seperator, false);
}
public static IEnumerable<string> SplitAsEnumerable(this string source, string seperator, bool returnSeperator)
{
if (!string.IsNullOrEmpty(source))
{
int pos = 0;
do
{
int newPos = source.IndexOf(seperator, pos, StringComparison.InvariantCultureIgnoreCase);
if (newPos == -1)
{
yield return source.Substring(pos);
break;
}
yield return source.Substring(pos, newPos - pos);
if (returnSeperator) yield return source.Substring(newPos, seperator.Length);
pos = newPos + seperator.Length;
} while (true);
}
}
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count)
{
List<T> items = new List<T>();
foreach (var item in source)
{
items.Add(item);
if (items.Count > count) items.RemoveAt(0);
}
return items;
}
}
}
编辑:有人指出这种方法可能不够高效,因为它需要迭代整个字符串。我也认为使用列表的RemoveAt(0)可能也不够高效。为了解决这个问题,代码可以被修改为向后搜索字符串。这将消除TakeLast函数的需求,因为我们可以直接使用Take。
高效利用空间的方法
private static void PrintLastNLines(string str, int n)
{
int idx = str.Length - 1;
int newLineCount = 0;
while (newLineCount < n)
{
if (str[idx] == 'n' && str[idx - 1] == '\\')
{
newLineCount++;
idx--;
}
idx--;
}
PrintFromIndex(str, idx + 3);
}
private static void PrintFromIndex(string str, int idx)
{
for (int i = idx; i < str.Length; i++)
{
if (i < str.Length - 1 && str[i] == '\\' && str[i + 1] == 'n')
{
Console.WriteLine();
i++;
}
else
{
Console.Write(str[i]);
}
}
Console.WriteLine();
}