StreamReader消耗的字节数

时间:2009-04-10 11:56:28

标签: c# .net

有没有办法知道StreamReader使用了多少字节的流?

我有一个项目,我们需要读取一个文件,其中包含文本标题,后跟二进制数据的开头。我最初尝试阅读此文件是这样的:

private int _dataOffset;
void ReadHeader(string path) 
{
    using (FileStream stream = File.OpenRead(path)) 
    {
        StreamReader textReader = new StreamReader(stream);

        do 
        {
            string line = textReader.ReadLine();
            handleHeaderLine(line);
        } while(line != "DATA") // Yes, they used "DATA" to mark the end of the header

        _dataOffset = stream.Position;
    }
}

private byte[] ReadDataFrame(string path, int frameNum) 
{
    using (FileStream stream = File.OpenRead(path)) 
    {
        stream.Seek(_dataOffset + frameNum * cbFrame, SeekOrigin.Begin);

        byte[] data = new byte[cbFrame];
        stream.Read(data, 0, cbFrame);

        return data;
    }
    return null;
}

问题在于,当我将_dataOffset设置为stream.Position时,我得到了StreamReader已读取的位置,而不是标头的末尾。我一想到它就有意义了,但我仍然需要知道标题的结尾在哪里,我不确定是否有办法做到这一点并仍然利用StreamReader。

5 个答案:

答案 0 :(得分:3)

你可以通过多种方式找出StreamReader实际返回的字节数(而不是从流中读取),这些都不是太直接我害怕。

  1. 获取textReader.CurrentEncoding.GetByteCount(totalLengthOfAllTextRead)的结果,然后在流中寻找此位置。
  2. 使用一些反射hackery来检索StreamReader对象的私有变量的值,该变量对应于内部缓冲区中的当前字节位置(与流的不同 - 通常在后面,但不超过等于当然)。根据.NET Reflector判断,此变量似乎名为bytePos
  3. 根本不打算使用StreamReader,而是实现在StreamBinaryReader偶数(BinaryReader之上构建的自定义ReadLine函数,保证永远不会比你要求的更进一步阅读。这个自定义函数必须通过char读取流char,所以你实际上必须使用低级Decoder对象(除非编码是ASCII / ANSI,在这种情况下,由于单个,事情有点简单-byte encoding)。
  4. 选项1将是我想象中效率最低的(因为你有效地重新编码刚解码的文本),而选项3最难实现,尽管可能是最优雅的。我可能会建议不要使用丑陋的反射黑客(选项2),即使它看起来很诱人,是最直接的解决方案,只需要几行。 (说实话,StreamReader类实际上应该通过公共属性公开这个变量,但是它没有。)所以最后,这取决于你,但方法1或3应该做到工作得很好......

    希望有所帮助。

答案 1 :(得分:1)

所以数据是utf8(StreamReader的默认编码)。这是一种多字节编码,因此IndexOf是不可取的。你可以:

Encoding.UTF8.GetByteCount(string)

到目前为止,为缺失的行结尾添加1或2个字节。

答案 2 :(得分:1)

如果你需要计算字节数,我会选择BinaryReader。您可以根据需要获取结果并进行转换,但我发现它的当前位置更可靠(因为它读取二进制文件,它不受字符集问题的影响)。

答案 3 :(得分:0)

所以你的最后一行包含'DATA'+未知数量的数据字节。您可以使用IndexOf()和上一个读取行来提取位置。然后重新调整stream.Position。

但在这种情况下,我不确定你是否应该使用ReadLine()。也许最好逐字节读取,直到达到'DATA'标记。

答案 4 :(得分:0)

可以轻松识别换行符,而无需首先解码流(除了一些编码很少用于文本文件,如EBCDIC,UTF-16,UTF-32),因此您只需将每行读取为字节然后解码整行:

using (FileStream stream = File.OpenRead(path)) {
   List<byte> buffer = new List<byte>();
   bool hasCr = false;
   bool done = false;
   while (!done) {
      int b = stream.ReadByte();
      if (b == -1) throw new IOException("End of file reached in header.");
      if (b == 13) {
         hasCr = true;
      } else if (b == 10 && hasCr) {
         string line = Encoding.UTF8.GetString(buffer.ToArray(), 0, buffer.Count);
         if (line == "DATA") {
            done = true;
         } else {
            HandleHeaderLine(line);
         }
         buffer.Clear();
         hasCr = false;
      } else {
         if (hasCr) buffer.Add(13);
         hasCr = false;
         buffer.Add((byte)b);
      }
   }
   _dataOffset = stream.Position;
}

您可以继续阅读数据,而不是关闭流并再次打开它。