StreamWriter和UTF-8字节顺序标记

Kev*_*vin 55 c# file-encodings

我遇到了StreamWriter和Byte Order Marks的问题.该文档似乎表明Encoding.UTF8编码已启用字节顺序标记,但是当写入文件时,一些文件具有标记,而其他文件没有.

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

this.Writer = new StreamWriter(this.Stream, System.Text.Encoding.UTF8);
Run Code Online (Sandbox Code Playgroud)

任何有关可能发生的事情的想法都将受到赞赏.

Hel*_*Sam 86

正如有人已经指出的那样,没有编码参数的调用就可以了.但是,如果您想要明确,请尝试以下方法:

using (var sw = new StreamWriter(this.Stream, new UTF8Encoding(false)))
Run Code Online (Sandbox Code Playgroud)

关键是构造一个新的UTF8Encoding(false),而不是使用Encoding.UTF8Encoding.这是为了控制是否应该添加BOM.

这与在没有编码参数的情况下调用StreamWriter相同,在内部它只是做同样的事情.

  • 哈哈,我想这给读者留下了一个练习:PI 认为我已经修复了错误。 (2认同)
  • 为什么是假的?这应该是真的。请查看下面Nik的回答。我不明白,这个答案如何获得充值选票,因为它提供了相反的答案。 (2认同)

cas*_*One 17

问题是由于您在上使用静态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());
Run Code Online (Sandbox Code Playgroud)

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

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

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


Jim*_*hel 13

我唯一一次看到构造函数没有添加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");
    }
}
Run Code Online (Sandbox Code Playgroud)

正如其他人所说,如果您使用StreamWriter(stream)构造函数而未指定编码,那么您将看不到BOM.


Nik*_*Nik 13

我的回答是基于HelloSam的,其中包含所有必要的信息.只有我相信OP要求的是如何确保将BOM发送到文件中.

因此,您需要传递true,而不是将false传递给UTF8Encoding ctor.

    using (var sw = new StreamWriter("text.txt", new UTF8Encoding(true)))
Run Code Online (Sandbox Code Playgroud)

尝试下面的代码,在十六进制编辑器中打开生成的文件,看看哪个包含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??????");
        }
    }
Run Code Online (Sandbox Code Playgroud)


Phi*_*ohl 6

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

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

我刚才也处于类似情况.我最终使用该Stream.Write方法而不是StreamWriter并Encoding.GetPreamble()在编写之前编写了结果Encoding.GetBytes(stringToWrite)


Rod*_*ero 5

我发现这个答案很有用(感谢@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();
}
Run Code Online (Sandbox Code Playgroud)