2012-03-26 98 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(); 
    }