为什么BinaryWriter在流的开头添加无用信息?如何避免这种情况发生?

14

我正在调试一些关于将对象的部分内容写入文件的问题,现在我已经将其简化到了只需打开文件并在其中写入"TEST"。我的做法大致如下:

static FileStream fs;
static BinaryWriter w;
fs = new FileStream(filename, FileMode.Create);
w = new BinaryWriter(fs);

w.Write("test");

w.Close();
fs.Close();

不幸的是,这最终会在文件前面添加一个框,看起来像这样:

测试,在前面有一个有趣的框。为什么会这样,我该如何避免?

编辑:它似乎没有在这里显示框,但是它是一个看起来像胡言乱语的Unicode字符。


10
实际回答你的问题:二进制写入器通过在字符串前面添加其长度来编写二进制字符串。这样,二进制读取器就知道接下来有多少字符串数据。 - Eric Lippert
4
如果你想把字符串作为字符数组而不是字符串进行写入,那么可以尝试使用w.Write("test".ToCharArray())来强制编写器将字符作为数组写出,而不是作为带长度前缀的字符串。 - Eric Lippert
5
有很多方法可以解决这个问题。你可以编写字符数组,也可以编写字节数组,还可以编写四个单独的字节,你可以做任何想做的事情。你可以控制二进制文件的格式,如果你不喜欢默认的字符串处理方式,可以自己实现。 - Eric Lippert
1
或者,您可以让读取程序检查前五个字节是否为04 74 65 73 74。 - Eric Lippert
Eric不要告诉程序员不要在意。这里的每个开发人员都关心他想要理解的格式,无论是二进制还是文本。Chris,请使用byte[]重载而不是字符串重载,以获得最好的控制。 - Петър Петров
显示剩余2条评论
9个回答

23

根据MSDN的说法,它们不是字节顺序标记,而是长度前缀:

public virtual void Write(string value);
将一个带长度前缀的字符串写入流中,如果想要从该点读取该字符串,就需要那个长度前缀。请参见BinaryReader.ReadString()

附加内容

似乎你实际上需要一个文件头检查器
  1. 这是一个问题吗?你可以读回长度前缀,所以作为文件类型检查它可以工作正常。

  2. 你可以使用Encoding.ASCII将字符串转换为byte[]数组,但然后你必须自己添加(暗示的)固定长度或者...手动添加前缀。读取byte[]后,你可以再次将其转换为字符串。

  3. 如果有大量文本需要写入,你甚至可以将TextWriter连接到同一个流上。但是要小心,Writers会关闭它们的流。我不建议这样做,但了解这一点很好。在这里,你还需要标记另一个读取器可以接管的点(固定头可以正常工作)。


8

这是因为BinaryWriter正在编写字符串的二进制表示,包括字符串的长度。如果您要编写直接数据(例如byte[]等),它将不包括该长度。

byte[] text = System.Text.Encoding.Unicode.GetBytes("test");
FileStream fs = new FileStream("C:\\test.txt", FileMode.Create);
BinaryWriter writer = new BinaryWriter(fs);
writer.Write(text);
writer.Close();

您会注意到它不包括长度。如果您要使用二进制写入器编写文本数据,则需要先进行转换。


8

起始字节是字符串的长度,它会以可变长度整数的形式写出。

如果字符串不超过127个字符,长度将存储为一个字节。当字符串达到128个字符时,长度将写成2,当长度达到一定值时,长度也会写成3或4。

问题在于您正在使用BinaryWriter,它可以写出BinaryReader稍后可以读取的数据。如果您想按照自己的格式编写输出,请删除像这样写字符串的功能,或完全停止使用BinaryWriter。


7
正如Henk在这个回答中指出的那样,这是字符串的长度(作为32位int)。
如果您不想要这个,您可以通过将每个字母的ASCII字符作为字节手动编写“TEST”,或者您可以使用:
System.Text.Encoding.UTF8.GetBytes("TEST")

请写出最终的数组(该数组不包含长度 int)


您拯救了我的一天,先生。非常感谢!bw.Write(System.Text.Encoding.UTF8.GetBytes(YourStringVariableToOutputToFile)); - Combine

2

0

你可以像这样将它保存为UTF8编码的字节数组:

...

BinaryWriter w = new BinaryWriter(fs);

w.Write(UTF8Encoding.Default.GetBytes("test"));

...

-1

那是字节序标记,很可能是因为流的编码设置为Unicode。


-1

请记住,Java字符串在内部编码为UTF-16。

因此,“test”实际上由字节0xff、0xfe(一起是字节顺序标记)、0x74、0x00、0x65、0x00、0x73、0x00、0x74、0x00组成。

您可能希望使用字节而不是字符流进行操作。


5
请记住,C#标签的意思是:“它不是Java”。 - H H

-2

看看Henk的回复 - 这是一个长度指示器,而不是字节顺序。 - Jon B
那么我了解了。根据最初的信息,BOM诊断相当合理。 - Steven Sudit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接