2012-03-26 101 views
16

什麼是有StreamReader.ReadLine()方法的功能的最佳方式,而是使用自定義(字符串)分隔符?C#的StreamReader「的ReadLine」對於自定義分隔符

我想這樣做:

String text; 
while((text = myStreamReader.ReadUntil("my_delim")) != null) 
{ 
    Console.WriteLine(text); 
} 

我試圖用Peek()StringBuilder,使我自己的,但它的效率太低。我正在尋找建議或可能是一個開源解決方案。

謝謝。

編輯

我應該澄清這...前面我已經看到this answer,不過,我不希望將整個文件讀入內存中。

+0

爲什麼不使用的ReadLine(),然後搜索字符串分隔符? – 2012-03-26 13:43:49

+0

通過使用'皮克()'和'StringBuilder'你基本上是在重複'的ReadLine()'做'裏面...... StreamReader'如此看來奇怪,我是如此緩慢;你可以發佈你已經嘗試過嗎? – digEmAll 2012-03-26 13:46:06

+0

效率低下?效率如何?表現不明顯? – 2012-03-26 13:48:12

回答

2

我想我會發表我自己的解決方案。它似乎工作得很好,代碼相對簡單。隨意發表評論。

public static String ReadUntil(this StreamReader sr, String delim) 
{ 
    StringBuilder sb = new StringBuilder(); 
    bool found = false; 

    while (!found && !sr.EndOfStream) 
    { 
     for (int i = 0; i < delim.Length; i++) 
     { 
      Char c = (char)sr.Read(); 
      sb.Append(c); 

      if (c != delim[i]) 
       break; 

      if (i == delim.Length - 1) 
      { 
       sb.Remove(sb.Length - delim.Length, delim.Length); 
       found = true; 
      } 
     } 
    } 

    return sb.ToString(); 
} 
+1

如果你在「found = true」之後加一個「break」,它會稍微清晰一些(對我來說)。需要少一些心理處理。 – 2014-04-15 18:48:36

+3

該解決方案僅適用於某些情況。例如,如果分隔符是「xy」,那麼該算法將會忽略「axxyb」中的分隔符,並且它將讀取直到流的結束。 – 2014-07-08 12:45:54

1

此代碼應該適用於任何字符串分隔符。

public static IEnumerable<string> ReadChunks(this TextReader reader, string chunkSep) 
{ 
    var sb = new StringBuilder(); 

    var sepbuffer = new Queue<char>(chunkSep.Length); 
    var sepArray = chunkSep.ToCharArray(); 

    while (reader.Peek() >= 0) 
    { 
     var nextChar = (char)reader.Read(); 
     if (nextChar == chunkSep[sepbuffer.Count]) 
     { 
      sepbuffer.Enqueue(nextChar); 
      if (sepbuffer.Count == chunkSep.Length) 
      { 
       yield return sb.ToString(); 
       sb.Length = 0; 
       sepbuffer.Clear(); 
      } 
     } 
     else 
     { 
      sepbuffer.Enqueue(nextChar); 
      while (sepbuffer.Count > 0) 
      { 
       sb.Append(sepbuffer.Dequeue()); 
       if (sepbuffer.SequenceEqual(chunkSep.Take(sepbuffer.Count))) 
        break; 
      } 
     } 
    } 
    yield return sb.ToString() + new string(sepbuffer.ToArray()); 
} 

免責聲明:

我做了一個小測試就這個,實際上是慢ReadLine方法,但我懷疑這是由於入隊/出隊/ sequenceEqual稱,在ReadLine方法可以應避免(因爲分隔符總是\r\n)。

再一次,我做了很少的測試,它應該可以工作,但不要把它當成完美的,隨時糾正它。 ;)

1

下面是我用一個簡單的解析器在需要的地方(通常,如果流不是最重要的,僅讀和.Split做這項工作),沒有太多優化,但應該很好地工作:
(它更像一個斯普利特方法 - 並在下面更筆記)

public static IEnumerable<string> Split(this Stream stream, string delimiter, StringSplitOptions options) 
    { 
     var buffer = new char[_bufffer_len]; 
     StringBuilder output = new StringBuilder(); 
     int read; 
     using (var reader = new StreamReader(stream)) 
     { 
      do 
      { 
       read = reader.ReadBlock(buffer, 0, buffer.Length); 
       output.Append(buffer, 0, read); 

       var text = output.ToString(); 
       int id = 0, total = 0; 
       while ((id = text.IndexOf(delimiter, id)) >= 0) 
       { 
        var line = text.Substring(total, id - total); 
        id += delimiter.Length; 
        if (options != StringSplitOptions.RemoveEmptyEntries || line != string.Empty) 
         yield return line; 
        total = id; 
       } 
       output.Remove(0, total); 
      } 
      while (read == buffer.Length); 
     } 

     if (options != StringSplitOptions.RemoveEmptyEntries || output.Length > 0) 
      yield return output.ToString(); 
    } 

...你可以簡單地切換到char分隔符如果需要的話只需更換

while ((id = text.IndexOf(delimiter, id)) >= 0) 

...與

while ((id = text.IndexOfAny(delimiters, id)) >= 0) 

(和id++而不是id+=和簽名this Stream stream, StringSplitOptions options, params char[] delimiters

...還刪除空等
希望它有助於

0
public static String ReadUntil(this StreamReader streamReader, String delimiter) 
    { 
     StringBuilder stringBuilder = new StringBuilder(); 

     while (!streamReader.EndOfStream) 
     { 
      stringBuilder.Append(value: (Char) streamReader.Read()); 

      if (stringBuilder.ToString().EndsWith(value: delimiter)) 
      { 
       stringBuilder.Remove(stringBuilder.Length - delimiter.Length, delimiter.Length); 
       break; 
      } 
     } 

     return stringBuilder.ToString(); 
    }