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类和StreamWriter类