有没有办法知道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。
答案 0 :(得分:3)
你可以通过多种方式找出StreamReader
实际返回的字节数(而不是从流中读取),这些都不是太直接我害怕。
textReader.CurrentEncoding.GetByteCount(totalLengthOfAllTextRead)
的结果,然后在流中寻找此位置。StreamReader
对象的私有变量的值,该变量对应于内部缓冲区中的当前字节位置(与流的不同 - 通常在后面,但不超过等于当然)。根据.NET Reflector判断,此变量似乎名为bytePos
。StreamReader
,而是实现在Stream
或BinaryReader
偶数(BinaryReader
之上构建的自定义ReadLine函数,保证永远不会比你要求的更进一步阅读。这个自定义函数必须通过char读取流char,所以你实际上必须使用低级Decoder
对象(除非编码是ASCII / ANSI,在这种情况下,由于单个,事情有点简单-byte encoding)。选项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;
}
您可以继续阅读数据,而不是关闭流并再次打开它。