2012-03-09 62 views
3

我需要从C#中的文件中读取最后一行x行到streamreader。做这个的最好方式是什么?从流式读取器中删除最后的x行

非常感谢!

+2

从文件末尾读取x个换行符,然后从该文件的开头读取,直到该位置。 – 2012-03-09 03:22:12

+0

您计划从文件中读取的记录是否存在某种统一性(常见记录长度,除\ n之外的任何内容)? – 2012-03-09 03:37:53

回答

3

如果它是一个大的文件,是否有可能只是寻找文件的结尾,并检查字符'\ n'的反向字符?我知道\ n和\ r \ n存在。我掀起了下面的代码,并在一个相当简单的文件上测试。你可以尝试对你有的文件进行测试吗?我知道我的解决方案看起来很长,但我认为你会发现它比从头开始阅读并重写整个文件要快。

public static void Truncate(string file, int lines) 
{ 
    using (FileStream fs = File.Open(file, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) 
    { 
     fs.Position = fs.Length; 

     // \n \r\n (both uses \n for lines) 
     const int BUFFER_SIZE = 2048; 

     // Start at the end until # lines have been encountered, record the position, then truncate the file 
     long currentPosition = fs.Position; 
     int linesProcessed = 0; 

     byte[] buffer = new byte[BUFFER_SIZE]; 
     while (linesProcessed < linesToTruncate && currentPosition > 0) 
     { 
      int bytesRead = FillBuffer(buffer, fs); 

      // We now have a buffer containing the later contents of the file 
      for (int i = bytesRead - 1; i >= 0; i--) 
      { 
       currentPosition--; 
       if (buffer[i] == '\n') 
       { 
        linesProcessed++; 
        if (linesProcessed == linesToTruncate) 
         break; 
       } 
      } 
     } 

     // Truncate the file 
     fs.SetLength(currentPosition); 
    } 
} 

private static int FillBuffer(byte[] buffer, FileStream fs) 
{ 
    if (fs.Position == 0) 
     return 0; 

    int bytesRead = 0; 
    int currentByteOffset = 0; 

    // Calculate how many bytes of the buffer can be filled (remember that we're going in reverse) 
    long expectedBytesToRead = (fs.Position < buffer.Length) ? fs.Position : buffer.Length; 
    fs.Position -= expectedBytesToRead; 

    while (bytesRead < expectedBytesToRead) 
    { 
     bytesRead += fs.Read(buffer, currentByteOffset, buffer.Length - bytesRead); 
     currentByteOffset += bytesRead; 
    } 

    // We have to reset the position again because we moved the reader forward; 
    fs.Position -= bytesRead; 
    return bytesRead; 
} 

由于您只在删除文件的末尾计划,似乎浪费改写一切,尤其是如果它是一个大文件和小N.当然,人们可以使论点是,如果有人想消除所有线路,然后从头到尾更高效。

+0

谢谢你,明天我会尝试第一件事,但它看起来会做我所需要的 – 2012-03-09 05:42:55

+0

工作得很好。感谢大家的帮助。 – 2012-03-09 06:08:53

3

您并不真正阅读INTO StreamReader。事实上,对于你要求的模式,根本不需要StreamReader。有System.IO.File具有有用的静态方法“readlines方法”,你可以改为利用:

IEnumerable<string> allBut = File.ReadLines(path).Reverse().Skip(5).Reverse(); 

以前的版本有缺陷,早在回应评论跟帖

List<string> allLines = File.ReadLines(path).ToList(); 
IEnumerable<string> allBut = allLines.Take(allLines.Count - 5); 
+0

你建议将整个文件作为高性能替代文件(ReadLines.Count _will_读取整个文件)? – 2012-03-09 03:34:14

+1

你说得对。我只是通过一些定时测试来运行它。第二种方法始终更快。谢谢。更新我的答案以删除第一个选项。 – xcud 2012-03-09 03:47:27

+0

+1 - 虽然通过在查找Chr(13) - 1字节时逐字节地逐字节处理它可能会变得更高性能,但这应该仍然比替代方法快。 – 2012-03-09 03:58:13

3

既然你是指在文件中的行,我假设它是一个文本文件。如果你只是想获得的线,你可以阅读到一个字符串数组,像这样:

string[] lines = File.ReadAllLines(@"C:\test.txt"); 

或者,如果你真的需要StreamReaders工作:

using (StreamReader reader = new StreamReader(@"C:\test.txt")) 
     { 
      while (!reader.EndOfStream) 
      { 
       Console.WriteLine(reader.ReadLine()); 
      } 
     } 
+0

“StreamReader”的使用应该足以假定OP正在讨论文本。 – 2012-03-09 03:32:12

+0

我想我不确定询问问题的人是否知道他们需要StreamReader,或者只知道他们需要阅读文件并在线进行快速搜索并看到StreamReader出现。但是,是的,你是正确的 – BryanJ 2012-03-09 04:37:29