如何在不循环ReadLine()的情况下读取文本文件的特定行号?

时间:2014-07-15 13:00:40

标签: c# .net parsing

是否有一个类允许您在C#中按行号读取行?

我了解StreamReader和TextFieldParser,但AFAIK不具备此功能。例如,如果我知道我的文本文件中的行号34572包含某些数据,那么不必调用StreamReader.ReadLine() 34572次就可以了。

3 个答案:

答案 0 :(得分:9)

除非文件具有精确且预先确定的格式,例如每条线具有相同的长度,否则无法在文本文件中进行搜索。

为了找到i th 行,您必须找到第一个i-1行结束。如果您对这些行结束的位置一无所知,那么您必须读取整个文件,直到i th 行。

答案 1 :(得分:5)

这不是C#的问题 - 这是行终止符的问题。没有办法跳到34572行,因为你不知道它何时开始 - 你唯一知道的是它在找到34571 \r\n之后开始。如果您需要此功能,则根本不想使用文本文件:)

一种简单(但仍然很慢)的方法是使用File.ReadLines(...)

var line = File.ReadLines(fileName).Skip(34571).FirstOrDefault();

然而,最好的方法是知道线的实际字节偏移量。如果您记住偏移量而不是行号,则可以简单地在流中搜索并避免读取不必要的数据。然后你就像往常一样继续阅读这条线:

streamReader.BaseStream.Seek(offset, SeekOrigin.Begin);

var line = streamReader.ReadLine();

如果文件仅附加(例如日志文件)并且您可以记住书签,这将非常有用。但是,只有在书签前没有修改文件时,它才会起作用。

总而言之,有三种选择:

  • 添加索引 - 包含一个包含所有(或部分)行启动偏移的表
  • 具有固定的行长度 - 这允许您在没有索引的情况下可预测地寻找;但是,这对unicode不起作用,所以现在它几乎没用了
  • 解析文件 - 这几乎相当于逐行读取文件,唯一的优化是你实际上不需要分配字符串 - 一个简单的可重用byte缓冲区就可以了。 / LI>

当性能很重要时,文本格式不是首选的原因 - 当您使用用户可编辑的通用文本格式时,第三个选项是唯一选项。因此,从JSON,XML,文本日志文件等中读取将始终意味着至少读取您想要的行。

答案 2 :(得分:0)

这可能不是你的情况,但是如果你知道每一行的长度,你将计算你寻找的行的起始索引字节,可能如下所示:

FileStream fs = new FileStream("fullFileName");

fs.Seek(startByte, SeekOrigin.Current)
for (long offset = startByte; offset < fs.Length; offset++)
{
     fs.Seek(-1, SeekOrigin.Current);
     char value = (char)((byte)fs.ReadByte());
    .
    .
    .
//To determine the end of each line you can use the conditions below
     if (value == 0xA)//  hex \n
     {
          if (offset == fs.Length)
              continue;

     }
     else if (value == 0xD)// hex \r
     {
         if (offset == fs.Length - 1)
              continue;
     }

}