C# 取字符串c的最后n行#

C# 取字符串c的最后n行#,c#,string,string-parsing,C#,String,String Parsing,我有一个长度不详的字符串 它的格式是 \nline \nline \nline 如果我不知道它有多长,我怎么能只画最后10行呢 用“\n”Split()分隔的一行,\n上的字符串,并获取结果数组的最后10个元素。如果这是在一个文件中,并且文件特别大,您可能希望有效地执行此操作。一种方法是向后读取文件,然后只读取前10行。您可以看到一个使用Jon Skeet的库执行此操作的示例 var result = text.Split('\n').Reverse().Take(10).ToArray();

我有一个长度不详的字符串

它的格式是

\nline
\nline
\nline
如果我不知道它有多长,我怎么能只画最后10行呢
用“\n”

Split()
分隔的一行,
\n
上的字符串,并获取结果数组的最后10个元素。

如果这是在一个文件中,并且文件特别大,您可能希望有效地执行此操作。一种方法是向后读取文件,然后只读取前10行。您可以看到一个使用Jon Skeet的库执行此操作的示例

var result = text.Split('\n').Reverse().Take(10).ToArray();

这里有一种方法,它的优点是不创建整个源字符串的副本,因此相当有效。大多数代码将与其他通用扩展方法一起放在一个类中,因此最终的结果是您可以用一行代码来完成

using 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;
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
命名空间控制台应用程序1
{
班级计划
{
静态void Main(字符串[]参数)
{
字符串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(x.SplitAsEnumerable(“\r\n”).TakeLast(10)”中的var行)
控制台写入线(行);
Console.ReadKey();
}
}
静态类LinqExtensions
{
公共静态IEnumerable SplitaEnumerable(此字符串源)
{
返回SplitAsEnumerable(源“,”);
}
公共静态IEnumerable SplitAsEnumerable(此字符串源,字符串分隔符)
{
返回SplitAsEnumerable(源、分隔符、false);
}
公共静态IEnumerable SplitAsEnumerable(此字符串源、字符串分隔符、布尔返回分隔符)
{
如果(!string.IsNullOrEmpty(源))
{
int pos=0;
做
{
int newPos=source.IndexOf(separator,pos,StringComparison.invariantCultureInogoreCase);
如果(newPos==-1)
{
收益返回源。子字符串(pos);
打破
}
收益返回源.子字符串(pos,newPos-pos);
if(returnseparator)产生返回源子串(newPos,separator.Length);
pos=新pos+分隔符长度;
}虽然(正确);
}
}
公共静态IEnumerable TakeLast(此IEnumerable源,int计数)
{
列表项=新列表();
foreach(源中的var项)
{
项目。添加(项目);
如果(items.Count>Count)items.RemoveAt(0);
}
退货项目;
}
}
}

EDIT:有人指出,这可能更有效,因为它会迭代整个字符串。我还认为带有列表的RemoveAt(0)可能也是低效的。为了解决这个问题,可以修改代码以向后搜索字符串。这将消除对TakeLast函数的需要,因为我们可以只使用Take。

随着字符串变大,避免处理无关紧要的字符变得更加重要。任何使用
string.Split
的方法都是低效的,因为必须处理整个字符串。一个有效的解决方案必须从后面贯穿整个字符串。这里有一个正则表达式方法

请注意,它返回一个
列表
,因为在返回结果之前需要对结果进行反转(因此使用
插入
方法)

private静态列表TakeLastLines(字符串文本,整数计数)
{
列表行=新列表();
Match Match=Regex.Match(文本“^.*$”,RegexOptions.Multiline | RegexOptions.rightoleft);
while(match.Success&&lines.Count
节省空间的方法

    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();
    }
私有静态void printlastnline(字符串str,int n)
{
int idx=结构长度-1;
int newLineCount=0;
while(newLineCount
动臂。比我写的要好得多。只要它说字符串不是很大,就简单快捷。你如何从数组中提取最后10个元素(没有for循环)顺便问一下字符串是什么HUGE@user1588670:只循环最后10个元素的for循环有什么问题
for(int i=arr.Length-10;i+1,尽管这将颠倒可能无人参与的行的顺序。您可以在末尾附加另一个
reverse
ToArray()
是多余的,因为OP没有提到他想要一个数组。@CodeSparke:因为
Skip
枚举了整个数组(巨大)数组,只取最后10个元素。
Reverse.take
将像
For循环一样实现,它只按相反的顺序循环最后10个元素,更有效,也更可读。@TimSchmelter每天学习关于LINQ的新知识;)谢谢你的解释。@ USE158670认为你提到输入字符串是“巨大”的,这根本不是正确的答案。您需要一个不复制数据的解决方案。我相信这实际上会复制两份
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;
}
    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();
    }