2008-11-21 36 views
1

請隨時糾正我,如果我在任何時候都錯了...在二進制流上實現ReadLine()的最有效方法是什麼?

我想讀取一個CSV(逗號分隔值)文件使用.NET文件I/O類。現在的問題是,這個CSV文件可能包含一些帶有軟回車符的字段(即單獨的\ r或\ n標記,而不是文本文件中用於結束行的標準\ r \ n),並且標準文本模式I/O類StreamReader不遵守標準約定,並將軟回車視爲硬回車,從而影響CSV文件的完整性。

現在使用BinaryReader類似乎是唯一的選擇,但BinaryReader沒有ReadLine()函數,因此需要自行實現ReadLine()。

我現在的做法是每次從流中讀取一個字符並填充StringBuilder,直到獲得\ r \ n(忽略所有其他字符,包括孤立的\ r或\ n),然後返回StringBuilder的字符串表示(使用ToString())。

但我想知道:這是實現ReadLine()函數的最有效方式嗎?請賜教。

+1

當你說「標準約定」時,你應該認識到它不是特別標準。在Unix上,「\ n」是普通的行結束符。 – 2008-11-21 13:10:52

+0

你真的有一個性能問題,或者這是一個過早優化的典型案例嗎? )。我沒有看到你提到性能問題 – 2008-11-21 13:10:58

+0

@Jon - 是的,我知道,謝謝。我的意思是窗口/ dos上的標準。 – 2008-11-21 13:39:13

回答

6

它可能是。就順序而言,它只經過一次每個字符,所以它將是O(n)(其中n是流的長度),所以這不是問題。要讀取單個字符,BinaryReader是您最好的選擇。

我會做的是使一個類

public class LineReader : IDisposable 
{ 
    private Stream stream; 
    private BinaryReader reader; 

    public LineReader(Stream stream) { reader = new BinaryReader(stream); } 

    public string ReadLine() 
    { 
     StringBuilder result = new StringBuilder(); 
     char lastChar = reader.ReadChar(); 
     // an EndOfStreamException here would propogate to the caller 

     try 
     { 
      char newChar = reader.ReadChar(); 
      if (lastChar == '\r' && newChar == '\n') 
       return result.ToString(); 

      result.Append(lastChar); 
      lastChar = newChar; 
     } 
     catch (EndOfStreamException) 
     { 
      result.Append(lastChar); 
      return result.ToString(); 
     } 
    } 

    public void Dispose() 
    { 
     reader.Close(); 
    } 
} 

或類似的東西。 (警告:代碼沒有經過測試,並且按原樣提供,沒有任何明示或暗示的保證。如果此程序證明有缺陷或毀壞了地球,您將承擔所有必要的維修,修理費用或更正)。

0

如何簡單地預處理文件?

用獨特的東西代替軟回車返回。

爲了記錄,CSV文件在數據中使用換行符,這是糟糕的設計。

+0

我認爲只要您在windows/dos上,CSV數據中的奇數行可能不是一個壞主意。這種設計已經有相當長的一段時間了。這是如何在excel中完成的,例如,如果您在單元格中有換行符。 (按Alt + Enter在單元格內引入換行符) – 2008-11-21 12:55:03

0

您可以一次讀取更大的塊,使用Encoder.GetString將其解碼爲字符串,然後使用string.Split(「\ r \ n」)分割爲多行,或者甚至使用字符串的頭部選取string.Substring(0,string.IndexOf(「\ r \ n」))並留下其餘的處理下一行。請記住將上一次讀取的最後一行添加到下一個讀取操作。

+0

基礎流已經將讀取緩衝到更大的塊,不是嗎? – configurator 2008-11-21 12:49:59

0

您的方法聽起來不錯。提高方法效率的一種方法可能是將每行存儲爲常規字符串(即不是StringBuilder),然後將整行字符串附加到StringBuilder。進一步的解釋見this article - StringBuilder不會自動成爲這裏的最佳選擇。

雖然它可能會很小。

1

您可能想要使用ODBC/OleDB連接來執行此操作。如果您將oledb連接的數據源指向包含csv文件的目錄,則可以查詢它,就好像每個CSV都是表格一樣。
請查看http://www.connectionstrings.com/?carrier=textfile>connectionstrings.com以獲得正確的連接字符串

0

下面是一個更快的編碼支持替代方案。它擴展了BinaryReader,因此您可以使用它來執行這兩個操作,讀取二進制塊,還可以直接在二進制流上執行像ReadLine一樣的StreamReader。

public class LineReader : BinaryReader 
{ 
    private Encoding _encoding; 
    private Decoder _decoder; 

    const int bufferSize = 1024; 
    private char[] _LineBuffer = new char[bufferSize]; 

    public LineReader(Stream stream, int bufferSize, Encoding encoding) 
     : base(stream, encoding) 
    { 
     this._encoding = encoding; 
     this._decoder = encoding.GetDecoder(); 
    } 

    public string ReadLine() 
    { 
     int pos = 0; 

     char[] buf = new char[2]; 

     StringBuilder stringBuffer = null; 
     bool lineEndFound = false; 

     while(base.Read(buf, 0, 2) > 0) 
     { 
      if (buf[1] == '\r') 
      { 
       // grab buf[0] 
       this._LineBuffer[pos++] = buf[0]; 
       // get the '\n' 
       char ch = base.ReadChar(); 
       Debug.Assert(ch == '\n'); 

       lineEndFound = true; 
      } 
      else if (buf[0] == '\r') 
      { 
       lineEndFound = true; 
      }      
      else 
      { 
       this._LineBuffer[pos] = buf[0]; 
       this._LineBuffer[pos+1] = buf[1]; 
       pos += 2; 

       if (pos >= bufferSize) 
       { 
        stringBuffer = new StringBuilder(bufferSize + 80); 
        stringBuffer.Append(this._LineBuffer, 0, bufferSize); 
        pos = 0; 
       } 
      } 

      if (lineEndFound) 
      { 
       if (stringBuffer == null) 
       { 
        if (pos > 0) 
         return new string(this._LineBuffer, 0, pos); 
        else 
         return string.Empty; 
       } 
       else 
       { 
        if (pos > 0) 
         stringBuffer.Append(this._LineBuffer, 0, pos); 
        return stringBuffer.ToString(); 
       } 
      } 
     } 

     if (stringBuffer != null) 
     { 
      if (pos > 0) 
       stringBuffer.Append(this._LineBuffer, 0, pos); 
      return stringBuffer.ToString(); 
     } 
     else 
     { 
      if (pos > 0) 
       return new string(this._LineBuffer, 0, pos); 
      else 
       return null; 
     } 
    } 

} 
1

這裏BinaryReader在類的擴展方法:

using System.IO; 
using System.Text; 

public static class BinaryReaderExtension 
{ 
    public static string ReadLine(this BinaryReader reader) 
    { 
     if (reader.IsEndOfStream()) 
      return null; 

     StringBuilder result = new StringBuilder(); 
     char character; 
     while(!reader.IsEndOfStream() && (character = reader.ReadChar()) != '\n') 
      if (character != '\r' && character != '\n') 
       result.Append(character); 

     return result.ToString(); 
    } 

    public static bool IsEndOfStream(this BinaryReader reader) 
    { 
     return reader.BaseStream.Position == reader.BaseStream.Length; 
    } 
} 

我沒有測試的所有條件,但該代碼爲我工作。

相關問題