C# StreamReader消耗的字节数

C# StreamReader消耗的字节数,c#,.net,C#,.net,有没有办法知道StreamReader使用了多少字节的流 我有一个项目,我们需要读取一个文件,该文件有一个文本头,后跟二进制数据的开头。我最初尝试读取此文件的内容如下: private int _dataOffset; void ReadHeader(string path) { using (FileStream stream = File.OpenRead(path)) { StreamReader textReader = new StreamReade

有没有办法知道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;
}

问题是,当我将
\u dataOffset
设置为
stream.Position
时,我得到的是StreamReader读取到的位置,而不是标题的末尾。我一想到这一点就明白了,但我仍然需要知道标题的结尾在哪里,我不确定是否有办法做到这一点,并且仍然可以利用StreamReader。

因此,您的最后一行包含“数据”+未知数量的数据字节。您可以使用IndexOf()和最后一个读取行提取位置。然后重新调整流位置

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

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

Encoding.UTF8.GetByteCount(string)

到目前为止,在您的数据中,为缺少的行尾添加1或2个字节。

您可以通过多种方式了解
StreamReader
实际返回的字节数(与从流中读取的字节数相反),但恐怕没有一种方式过于简单

  • 获取
    textReader.CurrentEncoding.GetByteCount(TotalEngthoFallTextRead)
    的结果,然后在流中查找此位置
  • 使用一些反射黑客来检索
    StreamReader
    对象的私有变量的值,该对象对应于内部缓冲区中的当前字节位置(与流不同-通常在后面,但当然不大于)。根据.NET Reflector判断,此变量的名称似乎是
    bytePos
  • 根本不用麻烦使用
    StreamReader
    ,而是在
    Stream
    BinaryReader
    之上实现定制的ReadLine函数(
    BinaryReader
    保证不会比您的请求读得更远)。这个自定义函数必须逐个字符地从流中读取字符,因此实际上必须使用低级的
    解码器
    对象(除非编码是ASCII/ANSI,在这种情况下,由于采用单字节编码,事情会稍微简单一些)
  • 选项1将是我想象中效率最低的(因为您实际上是在对刚刚解码的文本进行重新编码),而选项3是最难实现的,尽管可能是最优雅的。我可能会建议不要使用丑陋的反射黑客(选项2),尽管它看起来很诱人,是最直接的解决方案,而且只需要几行代码。(老实说,
    StreamReader
    类确实应该通过公共属性公开此变量,但可惜它没有。)因此,最终由您决定,但方法1或方法3都应该很好地完成这项工作


    希望有帮助。

    如果你需要计算字节数,我会使用二进制阅读器。您可以根据需要获取结果并对其进行转换,但我发现其当前位置的想法更可靠(因为它以二进制形式读取,因此不受字符集问题的影响)。

    换行符很容易识别,无需先解码流(除了一些很少用于文本文件的编码,如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;
    }
    
    使用(FileStream-stream=File.OpenRead(path)){
    列表缓冲区=新列表();
    bool-hasCr=false;
    bool done=false;
    而(!完成){
    int b=stream.ReadByte();
    如果(b==-1)抛出新的IOException(“在头中到达文件末尾”);
    如果(b==13){
    hasCr=真;
    }else if(b==10&&hasCr){
    string line=Encoding.UTF8.GetString(buffer.ToArray(),0,buffer.Count);
    如果(行==“数据”){
    完成=正确;
    }否则{
    扶手线;
    }
    buffer.Clear();
    hasCr=假;
    }否则{
    if(hasCr)缓冲区。添加(13);
    hasCr=假;
    buffer.Add((字节)b);
    }
    }
    _dataOffset=流位置;
    }
    

    不必关闭流并再次打开它,您当然可以继续读取数据。

    如果我使用字符串的字节计数,这正是我关心的问题。我不知道要为行终止符添加多少。这不起作用,有一些字节用于存储技术信息,如果您尝试,这些字节将丢失这样计算。例如。-文件开头有三个字节,这表明该文件采用unicode编码。这当然是我的退路,我只是想在实现它之前看看是否有更好的方法。这种方法只适用于ASCII/ANSI编码。对于其他编码,你真的应该使用解码器,正如我在文章中详细介绍的。此外,使用列表将是非常低效的。是的,它对一些不常用的编码不起作用,我将添加一个not-about。列表使用字节数组进行存储,因此没有什么是非常低效的。