【问题标题】:TextReader.Peek behaviour and detecting end of stream/readerTextReader.Peek 行为和检测流/阅读器的结束
【发布时间】:2013-10-02 14:42:57
【问题描述】:

当我使用文本阅读器时,检测我实际上处于数据末尾的最佳方法是什么?执行此操作的通常方法是 类似 以下,

    while(reader.Peek() != -1)
    {
       ///do stuff
    }

但是,msdn 文档 here 声明如下

表示要读取的下一个字符的整数,如果没有更多字符可用或读取器不支持查找,则为 -1。

所以我的问题是,您如何判断您是否真的处于阅读器数据的末尾,或者阅读器/基础流根本不支持搜索,因为这里的返回值似乎模棱两可?如果例如我有以下

    public void Parse(TextReader reader)
    {
         while(reader.Peek() != -1) //am I really at the end
         {
            //do stuff
         }
    }

    Parse(new StreamReader(new NetworkStream(....)));

因为 networkstream 不支持搜索。

还是我错过了什么?

编辑:

澄清一下,我可以使用更具体的 StreamReader 类轻松实现这一点,因为我可以检查 EoS。然而,为了让事情更笼统,我想使用 TextReader,所以我不仅仅局限于 StreamReader。但是 Peek 的语义似乎有点奇怪,如果不支持搜索,为什么它不直接抛出,为此为什么没有 TextReader 的 EoF 属性?

【问题讨论】:

  • 有什么理由需要使用Peek 而不是Read
  • 它是状态机的一部分,所以我可能想也可能不想在当前状态下使用该字节。所以 peek 是我在这里唯一的选择吗?无需维护单独的未使用字节堆栈。
  • 那么检查reader.BaseStream.CanSeek怎么样?
  • 请问:为什么这么多人使用.Peek 而不是.EndOfStream 来检查是否到达流的尽头?使用.Peek有什么优势吗?
  • textreader 不公开基本流,因为它可能没有基本流(在字符串阅读器的情况下)。我认为这才是引发这个问题的真正动机。

标签: c# .net textreader


【解决方案1】:

除非您正在使用 Peek() 查找特定值 为什么不使用 .Read()

例如

string line;
System.IO.StreamReader file = new System.IO.StreamReader(strfn);
while((line = file.ReadLine()) != null)
{
  this.richTextBox1.AppendText(line+"\n");//you can replace this line to fit your UseCase
}

如果你想要一个更简洁的例子来说明如何做到这一点,你可以做一些我在下面发布的可读的事情,你可以插入你自己的文本文件值并调试它以查看它是否可以工作。阅读和写作

string tempFile = Path.GetTempFileName();
using(var sr = new StreamReader("file.txt"))
{
  using(var sw = new StreamWriter(tempFile))
  {
    string line;
    while((line = sr.ReadLine()) != null)
    {
         if(line != "BlaBlaBla")
             sw.WriteLine(line);
    }
  }
}

这是您可以尝试的另一种选择

来自Stream,如果你Read(buffer, offset, count),你会得到一个非阳性结果,如果你Peek()你会得到一个否定结果。

使用BinaryReaderthe documentation 建议PeekChar() 应该返回负数:

返回值

类型:System.Int32 下一个可用字符,如果没有更多可用字符或流不支持查找,则为 -1。

你确定这不是一个损坏的流吗?即剩余数据不能从给定的编码形成完整的char

【讨论】:

  • 虽然作为解决方案,但问题是一行可以任意长。想象一个没有回车和换行的 1 Tb 文件,这将尝试将其加载到内存中。
【解决方案2】:

这真的取决于你在解析中做什么。

我通常只会Read,看看读了多少。我建议不要一次读一个字符:

char[] buffer = new char[1024 * 16];
int charsRead;
while ((charsRead = read.Read(buffer, 0, buffer.Length)) > 0)
{
    // Process buffer, only as far as charsRead
}

【讨论】:

  • 我不明白 Read() 的问题是什么,因为提供的 Textreader 实现是内部缓冲的。或者我的 C# 3.0 书告诉我。我确实觉得很烦人,我必须首先执行 tmp = Read() 以便检查 -1,然后执行 c = (char) tmp 以获得我想要的,但这似乎比检查是否 c = 更安全/更干净= char.MaxValue()
  • 它可能被缓冲了,是的。不过,一次读取缓冲区对我来说感觉更干净......为什么在你真的不需要时依赖缓冲?除非您明确包含缓冲,否则由于实现细节的更改,您很容易遇到令人讨厌的性能问题。
  • 嗯,编写所有额外的代码/逻辑只是为了重新实现框架已经提供的功能似乎违背了使用框架的目的。 (感觉就像回到 C++。)在外循环内部,您需要另一个这样的循环,对吧? for (int i=0; i
  • @JCoombs:实际上通常不会——您通常可以批量处理字符。当然,这取决于你在做什么——但我的经验是,使用“批量”Read 和“一次一个字符”Read 一样容易(有时更容易) . (或者,通过ReadLine 一次读取一行 - 这通常比任何一个都更容易。)而且我绝对不会只是投射Read,因为这会删除有关您是否已经实际上到达了流的尽头。我会首先关注正确性
  • 有趣。我通常发现我一次处理一行文本或一个字符。拥有缓冲区的好处是能够看到前方(比如多个 Peek()),但我不知道它是如何工作的,因为当你碰巧接近缓冲区的末尾时它会中断,对吧?
【解决方案3】:

应该是 reader.Read() == -1 no more 否则字符存在。

【讨论】:

    【解决方案4】:

    如果您只需要读取和处理所有数据直到流结束,那么您应该直接使用Read,如果没有更多字符可用,则返回-1。

    int nextByte;
    while ((nextByte = reader.ReadByte()) != -1)
        // Process nextByte here.
    

    编辑:另一种具体检查阅读器是否支持搜索的方法是检查底层流:

    bool canSeek = reader.BaseStream.CanSeek;
    

    如果这返回true,那么Peek 应该只在到达流末尾时返回-1。

    【讨论】:

    • 他专门问的是TextReader而不是StreamReader。
    猜你喜欢
    • 2023-04-06
    • 1970-01-01
    • 2019-04-23
    • 2012-10-13
    • 2012-06-22
    • 1970-01-01
    • 2014-08-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多