我想使用两个不同的编写器处理一种“混合格式”文件(这个问题与我之前关于如何编写此类文件的问题相关:使用两个“using”语句和两个编写器写入同一个文件 )
我的问题是,当我打开一个文件,然后创建一个
StreamReader
来读取它时,我在该文件上创建的每个其他读取器都会获得一个空流,并且无法读取任何内容。
最小示例如下。假设我们有一个包含两行的文件,第一行说
first line
,第二行说 second line
。
然后,写下以下内容:
using (FileStream file = new(fileName, FileMode.Open, FileAccess.Read))
{
List<string> strings = new();
using (StreamReader reader = new StreamReader(file, leaveOpen: true))
{
strings.Add(reader.ReadLine());
}
using (StreamReader reader = new StreamReader(file, leaveOpen: true))
{
strings.Add(reader.ReadLine());
}
}
我希望列表
strings
包含元素"first line"
和"second line"
。相反,当我运行代码时,我得到第一个元素 "first line"
,但 strings
的第二个元素是 null
。 我显然不明白这里的一些东西,但我不知道是什么。
StreamReader
将一次读取文件的一大块(否则它必须逐个字符地读取才能找到行结尾,这将是非常低效的)。因此,您不能依赖它在任何特定点留下 FileStream 的位置。
OP 已澄清他们正在读取NRRD 格式的数据。这包含一个标头列表,通过两个换行符与二进制数据分隔:
NRRD000X
<field>: <desc>
<field>: <desc>
# <comment>
...
<field>: <desc>
<key>:=<value>
<key>:=<value>
<key>:=<value>
# <comment>
<data><data><data><data><data><data>...
(这类似于读取 HTTP 标头,然后读取正文)。
阅读此内容的最简单方法是可能扫描文件,查找彼此相邻的两个换行符。请记住,NRRD 中的换行符可以是 LF 和 CRLF。
一旦获得该位置,您可以将文件的第一部分作为文本读取,然后返回到二进制数据的开头并将其余部分作为二进制读取。不幸的是,这会有点啰嗦,但不幸的是,这可能是不可避免的。
或者,像下面这样的东西似乎也有效。这使用
StreamReader
来处理计算换行符是什么的混乱事务(LF 与 CRLF 等),但通过给它一个 MemoryStream
来读取来限制它,我们一次只添加一个字节。
由于
StreamReader.ReadLine
返回以 EOF 和换行符结尾的行,我们还必须检查 StreamReader.EndOfStream
来告诉我们它是否读取了已到达输入末尾的行。
byte[] b = new byte[1];
int bufferPosition = 0;
while (true)
{
int bytesRead = file.Read(b, 0, b.Length);
if (bytesRead == 0)
{
// We ran out of file data before reaching the end of the headers
break;
}
buffer.Write(b, 0, b.Length);
buffer.Position = bufferPosition;
reader.DiscardBufferedData();
string? line = reader.ReadLine();
if (line != null && !reader.EndOfStream)
{
Console.WriteLine(line);
bufferPosition = (int)buffer.Length - 1;
if (reader.ReadLine() == "")
{
break;
}
}
}