我遇到了StreamWriter和Byte Order Marks的问题。文档似乎表明Encoding.UTF8编码已启用字节顺序标记,但在写入文件时,某些文件具有标记而其他文件没有。
我正在以下列方式创建流编写器:
this.Writer = new StreamWriter(this.Stream, System.Text.Encoding.UTF8);
任何有关可能发生的事情的想法都将受到赞赏。
答案 0 :(得分:79)
正如某人已经指出的那样,没有编码参数的调用就可以了。 但是,如果您想要明确,请尝试以下方法:
using (var sw = new StreamWriter(this.Stream, new UTF8Encoding(false)))
关键是构造一个新的UTF8Encoding(false),而不是使用Encoding.UTF8Encoding。这是为了控制是否应该添加BOM。
这与没有编码参数调用StreamWriter相同,在内部它只是做同样的事情。
答案 1 :(得分:16)
问题是由于您使用UTF8
property上的静态Encoding
class。
在Encoding
属性返回的UTF8
类的实例上调用GetPreamble
method时,它返回字节顺序标记(三个字符的字节数组)并写入在将任何其他内容写入流之前的流(假设是新流)。
您可以通过自己创建UTF8Encoding
class的实例来避免这种情况,如下所示:
// As before.
this.Writer = new StreamWriter(this.Stream,
// Create yourself, passing false will prevent the BOM from being written.
new System.Text.UTF8Encoding());
根据default parameterless constructor(强调我的)的文档:
此构造函数创建一个不提供Unicode字节顺序标记的实例,并且在检测到无效编码时不会引发异常。
这意味着对GetPreamble
的调用将返回一个空数组,因此不会将BOM写入基础流。
答案 2 :(得分:12)
我唯一一次看到构造函数没有添加UTF-8 BOM就是当你调用它时流不在位置0。例如,在下面的代码中,不写入BOM:
using (var s = File.Create("test2.txt"))
{
s.WriteByte(32);
using (var sw = new StreamWriter(s, Encoding.UTF8))
{
sw.WriteLine("hello, world");
}
}
正如其他人所说,如果您使用StreamWriter(stream)
构造函数而未指定编码,那么您将看不到BOM。
答案 3 :(得分:12)
我的回答是基于HelloSam的,其中包含所有必要的信息。 只有我相信OP要求的是如何确保将BOM发送到文件中。
因此,您需要传递true,而不是将false传递给UTF8Encoding ctor。
using (var sw = new StreamWriter("text.txt", new UTF8Encoding(true)))
尝试下面的代码,在十六进制编辑器中打开生成的文件,看看哪一个包含BOM,哪些不包含。
class Program
{
static void Main(string[] args)
{
const string nobomtxt = "nobom.txt";
File.Delete(nobomtxt);
using (Stream stream = File.OpenWrite(nobomtxt))
using (var writer = new StreamWriter(stream, new UTF8Encoding(false)))
{
writer.WriteLine("HelloПривет");
}
const string bomtxt = "bom.txt";
File.Delete(bomtxt);
using (Stream stream = File.OpenWrite(bomtxt))
using (var writer = new StreamWriter(stream, new UTF8Encoding(true)))
{
writer.WriteLine("HelloПривет");
}
}
答案 4 :(得分:5)
您是否为每个文件使用相同的StreamWriter构造函数?因为文档说:
要使用UTF-8编码和BOM创建StreamWriter,请考虑使用指定编码的构造函数,例如StreamWriter(String,Boolean,Encoding)。
前一段时间我处于类似情况。在编写Encoding.GetPreamble()
Stream.Write
方法而不是StreamWriter并编写了Encoding.GetBytes(stringToWrite)
的结果
答案 5 :(得分:3)
我发现这个答案很有用(感谢@Philipp Grathwohl和@Nik),但在我的情况下,我正在使用FileStream来完成任务,因此,生成BOM的代码如下:
using (FileStream vStream = File.Create(pfilePath))
{
// Creates the UTF-8 encoding with parameter "encoderShouldEmitUTF8Identifier" set to true
Encoding vUTF8Encoding = new UTF8Encoding(true);
// Gets the preamble in order to attach the BOM
var vPreambleByte = vUTF8Encoding.GetPreamble();
// Writes the preamble first
vStream.Write(vPreambleByte, 0, vPreambleByte.Length);
// Gets the bytes from text
byte[] vByteData = vUTF8Encoding.GetBytes(pTextToSaveToFile);
vStream.Write(vByteData, 0, vByteData.Length);
vStream.Close();
}
答案 6 :(得分:2)
似乎如果文件已经存在且不包含BOM,那么在覆盖时它将不包含BOM,换句话说,StreamWriter在覆盖文件时会保留BOM(或者它不存在)。
答案 7 :(得分:0)
请你说明它不生产它的情况?唯一没有出现前导码的情况是,当没有任何内容写入作者时(Jim Mischel似乎找到了另一个,合乎逻辑的,更可能是你的问题,看到它的答案)。
我的测试代码:
var stream = new MemoryStream();
using(var writer = new StreamWriter(stream, System.Text.Encoding.UTF8))
{
writer.Write('a');
}
Console.WriteLine(stream.ToArray()
.Select(b => b.ToString("X2"))
.Aggregate((i, a) => i + " " + a)
);
答案 8 :(得分:0)
阅读SteamWriter的源代码后,您需要确定您正在创建一个新文件,然后字节顺序标记会添加到文件中。
https://github.com/dotnet/runtime/blob/6ef4b2e7aba70c514d85c2b43eac1616216bea55/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs#L267
Flush方法中的代码
if (!_haveWrittenPreamble)
{
_haveWrittenPreamble = true;
ReadOnlySpan 前导码 = _encoding.Preamble;
if (preamble.Length > 0)
{
_stream.Write(前导);
}
}
https://github.com/dotnet/runtime/blob/6ef4b2e7aba70c514d85c2b43eac1616216bea55/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs#L129
代码设置_haveWrittenPreamble
// 如果我们要追加到一个已经有数据的流,不要
写
// 序言。
如果 (_stream.CanSeek && _stream.Position > 0)
{
_haveWrittenPreamble = true;
}