73

我遇到了 StreamWriter 和字节顺序标记的问题。该文档似乎声明 Encoding.UTF8 编码启用了字节顺序标记,但是在写入文件时,有些有标记,而另一些则没有。

我正在通过以下方式创建流编写器:

this.Writer = new StreamWriter(this.Stream, System.Text.Encoding.UTF8);

任何关于可能发生的事情的想法将不胜感激。

4

10 回答 10

114

正如有人已经指出的那样,在没有编码参数的情况下调用就可以了。但是,如果你想明确,试试这个:

using (var sw = new StreamWriter(this.Stream, new UTF8Encoding(false)))

要禁用 BOM,关键是使用 构造new UTF8Encoding(false),而不仅仅是 Encoding.UTF8Encoding。这与在没有编码参数的情况下调用 StreamWriter 相同,在内部它只是在做同样的事情。

要启用 BOM,请new UTF8Encoding(true)改用。

更新:从 Windows 10 v1903 开始​​,当在 notepad.exe 中保存为 UTF-8 时,BOM 字节现在是一个可选功能。

于 2012-07-25T17:14:51.500 回答
18

问题是由于您在class上使用静态UTF8属性Encoding

当对该属性返回的类的实例调用该GetPreamble方法时,它会返回字节顺序标记(三个字符的字节数组)并在任何其他内容写入流之前写入流(假设是一个新流)。EncodingUTF8

UTF8Encoding您可以通过自己创建类的实例来避免这种情况,如下所示:

// As before.
this.Writer = new StreamWriter(this.Stream, 
    // Create yourself, passing false will prevent the BOM from being written.
    new System.Text.UTF8Encoding());

根据默认无参数构造函数的文档(强调我的):

此构造函数创建一个不提供 Unicode 字节顺序标记的实例,并且在检测到无效编码时不会引发异常。

这意味着调用GetPreamble将返回一个空数组,因此不会将 BOM 写入底层流。

于 2013-03-23T05:27:25.657 回答
17

我唯一一次看到构造函数不添加 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。

于 2011-03-10T21:45:14.873 回答
16

我的答案基于 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Привет");
        }
    }
于 2014-03-19T19:31:27.737 回答
6

您是否对每个文件都使用相同的 StreamWriter 构造函数?因为文档说:

要使用 UTF-8 编码和 BOM 创建 StreamWriter,请考虑使用指定编码的构造函数,例如 StreamWriter(String, Boolean, Encoding)。

前段时间我也遇到过类似的情况。我最终使用该Stream.Write方法而不是 StreamWriter 并Encoding.GetPreamble()在编写之前编写了结果Encoding.GetBytes(stringToWrite)

于 2011-03-10T21:40:33.477 回答
4

我发现这个答案很有用(感谢@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();
}
于 2014-12-02T14:28:02.703 回答
2

似乎如果文件已经存在并且不包含 BOM,那么在覆盖时它不会包含 BOM,换句话说,StreamWriter 在覆盖文件时会保留 BOM(或它不存在)。

于 2011-06-23T13:59:11.193 回答
1

您能否展示它不生产它的情况?我能找到的唯一不存在序言的情况是没有任何东西写给作者(吉姆米歇尔似乎找到了另一个,合乎逻辑的,更有可能是你的问题,看看它的答案)。

我的测试代码:

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)
    );
于 2011-03-10T21:45:46.853 回答
1

使用 Encoding.Default 而不是 Encoding.UTF8 解决了我的问题

于 2021-11-10T11:04:11.457 回答
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 preamble = _encoding.Preamble;
if (preamble.Length > 0)
{
_stream.Write(preamble);
}
}

https://github.com/dotnet/runtime/blob/6ef4b2e7aba70c514d85c2b43eac1616216bea55/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs#L129
代码设置_haveWrittenPreamble的值

// 如果我们要追加到已经有数据的 Stream,不要写
// 序言。
if (_stream.CanSeek && _stream.Position > 0)
{
_haveWrittenPreamble = true;
}

于 2021-04-13T05:32:46.180 回答