C#如何使用流读取器跳过多行文本文件中的行?

23

我有一个程序,它读取文本文件并将其处理为分成的部分。

所以问题是如何更改程序,以允许程序在使用Stream Reader读取文件时跳过文件的前5行?

请问有人能提供相关代码吗?谢谢!

代码:

class Program
{
    static void Main(string[] args)
    {
        TextReader tr = new StreamReader(@"C:\Test\new.txt");

        String SplitBy = "----------------------------------------";

        // Skip first 5 lines of the text file?
        String fullLog = tr.ReadToEnd();

        String[] sections = fullLog.Split(new string[] { SplitBy }, StringSplitOptions.None);

        //String[] lines = sections.Skip(5).ToArray();

        foreach (String r in sections)
        {
            Console.WriteLine(r);
            Console.WriteLine("============================================================");
        }
    }
}

2
使用被注释的代码行有什么问题? - Ilia G
展示给专家们看,.split方法不起作用。 - JavaNoob
Split() 方法为什么不起作用?当然,在处理大文件时它的效率非常低下,但它是可用的。 - Ilia G
6个回答

28

请尝试以下方法

// Skip 5 lines
for(var i = 0; i < 5; i++) {
  tr.ReadLine();
}

// Read the rest
string remainingText = tr.ReadToEnd();

13

如果行数固定,则最有效的方法如下:

using( Stream stream = File.Open(fileName, FileMode.Open) )
{
    stream.Seek(bytesPerLine * (myLine - 1), SeekOrigin.Begin);
    using( StreamReader reader = new StreamReader(stream) )
    {
        string line = reader.ReadLine();
    }
}

如果行的长度不同,那么您只需按以下方式逐行读取它们:

using (var sr = new StreamReader("file"))
{
    for (int i = 1; i <= 5; ++i)
        sr.ReadLine();
}

4
请注意,与其他解决方案不同的是,这种方法不会读取您跳过的行,因此比其他方法更快。 - Mathieson

8

如果您想在程序中多次使用它,则可以考虑创建一个自定义类,继承自StreamReader并具有跳过行的能力。

可以使用以下代码:

class SkippableStreamReader : StreamReader
{
    public SkippableStreamReader(string path) : base(path) { }

    public void SkipLines(int linecount)
    {
        for (int i = 0; i < linecount; i++)
        {
            this.ReadLine();
        }
    }
}

接下来,您可以使用SkippableStreamReader的函数跳过行。

例如:

SkippableStreamReader exampleReader = new SkippableStreamReader("file_to_read");

//do stuff
//and when needed
exampleReader.SkipLines(number_of_lines_to_skip);

1
我更倾向于使用扩展方法。 - Martin Braun

6
我会为列表增加两个建议。
如果总是有一个文件,并且你只需要读取它,我建议使用以下方法:
var lines = File.ReadLines(@"C:\Test\new.txt").Skip(5).ToArray();

File.ReadLines不会阻止其他人访问文件,并且只加载必要的行到内存中。

如果您的流可能来自其他来源,我建议采用这种方法:

class Program
{
    static void Main(string[] args)
    {
        //it's up to you to get your stream
        var stream = GetStream();

        //Here is where you'll read your lines. 
        //Any Linq statement can be used here.
        var lines = ReadLines(stream).Skip(5).ToArray();

        //Go on and do whatever you want to do with your lines...
    }
}

public IEnumerable<string> ReadLines(Stream stream)
{
    using (var reader = new StreamReader(stream))
    {
        while (!reader.EndOfStream)
        {
            yield return reader.ReadLine();
        }
    }
}

一旦您完成对 Iterator 块的使用,它将自动清除。 这里 是 Jon Skeet 的文章,详细介绍该过程的实现方式(请向下滚动至“最后…”部分)。


1
StreamReaderReadLineReadToEnd 方法实际上会读取字节到内存中,即使您不处理这些行,它们也会被加载,这会影响应用程序在处理大文件(10+ MB)时的性能。
如果你想跳过特定数量的行,你需要知道你想要移动的文件位置,这给了你两个选项:
  1. 如果你知道行的长度,你可以计算出位置并使用 Stream.Seek 移动到那里。这是跳过流内容而不读取它的最有效的方法。问题在于你很少知道行的长度。
var linesToSkip = 10;
using(var reader = new StreamReader(fileName) )
{
    reader.BaseStream.Seek(lineLength * (linesToSkip - 1), SeekOrigin.Begin);
    var myNextLine = reader.ReadLine();
    // TODO: process the line
}
  1. 如果您不知道行长,那么您需要逐行阅读并跳过它们,直到达到所需的行号。问题在于,如果行号很高,则会影响性能。
var linesToSkip = 10;
using (var reader = new StreamReader(fileName))
{
    for (int i = 1; i <= linesToSkip; ++i)
        reader.ReadLine();

    var myNextLine = reader.ReadLine();
    // TODO: process the line
}

如果您只需要跳过所有内容,而不将所有内容读入内存,则应执行此操作:

using(var reader = new StreamReader(fileName) )
{
   reader.BaseStream.Seek(0, SeekOrigin.End);

   // You can wait here for other processes to write into this file and then the ReadLine will provide you with that content

   var myNextLine = reader.ReadLine();
   // TODO: process the line
}

1
谢谢您的回答。我需要跳过33亿行,并且可以近似计算字节总数,所以这真的为我节省了很多时间。 - Ali

1
我猜这很简单:
    static void Main(string[] args)
    {
        var tr = new StreamReader(@"C:\new.txt");

        var SplitBy = "----------------------------------------";

        // Skip first 5 lines of the text file?
        foreach (var i in Enumerable.Range(1, 5)) tr.ReadLine();
        var fullLog = tr.ReadToEnd(); 

        String[] sections = fullLog.Split(new string[] { SplitBy }, StringSplitOptions.None);

        //String[] lines = sections.Skip(5).ToArray();

        foreach (String r in sections)
        {
            Console.WriteLine(r);
            Console.WriteLine("============================================================");
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接