2012-03-09 31 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