C# StreamReader,自定义分隔符的“ReadLine”

Posted

技术标签:

【中文标题】C# StreamReader,自定义分隔符的“ReadLine”【英文标题】:C# StreamReader, "ReadLine" For Custom Delimiters 【发布时间】:2012-04-10 00:20:03 【问题描述】:

拥有StreamReader.ReadLine() 方法的功能但使用自定义(字符串)分隔符的最佳方式是什么?

我想做这样的事情:

String text;
while((text = myStreamReader.ReadUntil("my_delim")) != null)

   Console.WriteLine(text);

我尝试使用Peek()StringBuilder 自己制作,但效率太低。我正在寻找建议或可能的开源解决方案。

谢谢。

编辑

我应该早点澄清这一点...我见过this answer,但是,我不想将整个文件读入内存。

【问题讨论】:

为什么不使用 ReadLine() 然后在字符串中搜索分隔符? 通过使用Peek()StringBuilder,您基本上是在复制ReadLine()StreamReader 中所做的事情......所以对我来说这么慢似乎很奇怪;您可以发布您尝试过的内容吗? 效率低下?效率低到什么程度?性能是否明显不足? 复制:***.com/questions/6655246/… @AdamKing - 不是重复的 - OP 特别想要一个字符串分隔符,而不是字符分隔符 【参考方案1】:

我想我会发布自己的解决方案。它似乎工作得很好,代码也比较简单。欢迎发表评论。

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();

【讨论】:

如果你在 "found = true" 之后加上一个 "break" 会更清楚(对我来说)。需要更少的心理处理。 此解决方案仅在某些情况下有效。例如,如果分隔符是“xy”,那么这个算法将错过“axxyb”中的分隔符,它会一直读取到流的末尾。【参考方案2】:

此代码适用于任何字符串分隔符。

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 方法慢,但我怀疑这是由于ReadLine 方法中的enqueue/dequeue/sequenceEqual 调用可以避免(因为分隔符总是\r\n)。

再次,我做了一些测试,它应该可以工作,但不要认为它是完美的,请随时纠正它。 ;)

【讨论】:

【参考方案3】:

这是我在需要的地方使用的一个简单的解析器(通常如果流式传输不是最重要的,只需读取并且 .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();
    

...如果需要,您可以简单地切换到字符分隔符,只需替换

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

...与

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

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

...也删除空等。 希望对你有帮助

【讨论】:

【参考方案4】:
    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();
    

【讨论】:

以上是关于C# StreamReader,自定义分隔符的“ReadLine”的主要内容,如果未能解决你的问题,请参考以下文章

C#中,StreamReader如何自定义从某一行读取文字?就是假设x里存有一有效的整数,就读x行。

C# Streamreader:连接意外关闭

C# StreamReader类和StreamWriter类

对十进制数使用自定义字符串格式c#

使用 C# 将文件加载到字符串变量中时,是不是需要显式关闭 C# 中的 StreamReader?

C# 文件流 streamreader如何读取文本指定行的数据?