C# StreamWriter和UTF-8字节顺序标记

C# StreamWriter和UTF-8字节顺序标记,c#,file-encodings,C#,File Encodings,我对StreamWriter和字节顺序标记有问题。文档似乎说明Encoding.UTF8编码已启用字节顺序标记,但在写入文件时,有些文件具有标记,而另一些文件没有 我以以下方式创建流编写器: this.Writer = new StreamWriter(this.Stream, System.Text.Encoding.UTF8); 任何关于可能发生的事情的想法都将不胜感激 是否对每个文件使用相同的StreamWriter构造函数?因为文件上说: < >使用UTF-8编码和BOM创建流写器,考

我对StreamWriter和字节顺序标记有问题。文档似乎说明Encoding.UTF8编码已启用字节顺序标记,但在写入文件时,有些文件具有标记,而另一些文件没有

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

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

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

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

< >使用UTF-8编码和BOM创建流写器,考虑使用指定编码的构造函数,如StreamWriter(String,BooLoin,编码)。


不久前我也遇到过类似的情况。在编写
Encoding.GetBytes(stringToWrite)
之前,我使用了该方法而不是StreamWriter,并编写了
Encoding.getPremission()的结果。例如,在下面的代码中,没有写入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。

您能否显示一种它不生成BOM的情况?我能找到的唯一一个序言没有出现的情况是,没有人给作者写任何东西(吉姆·米切尔似乎已经找到了另一个更符合逻辑、更可能是你的问题的答案)

我的测试代码:

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)
    );

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

正如有人已经指出的,不使用编码参数调用就可以了。 但是,如果要明确,请尝试以下方法:

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

要启用BOM,请改用新的UTF8Encoding(true)


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

问题是由于您在上使用了静态

UTF8
属性返回的
Encoding
类的实例上调用时,它返回字节顺序标记(三个字符的字节数组),并在将任何其他内容写入流(假定为新流)之前写入流

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

// 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写入底层流。

我的答案基于HelloSam的一个包含所有必要信息的数组。 我只相信OP要求的是如何确保BOM被发送到文件中

因此,您需要传递true,而不是将false传递给utf8

    using (var sw = new StreamWriter("text.txt", new UTF8Encoding(true)))
尝试下面的代码,在十六进制编辑器中打开生成的文件,看看哪个包含BOM,哪个不包含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Привет");
        }
    }

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

阅读SteamWriter的源代码后,您需要确保您正在创建一个新文件,然后字节顺序标记将添加到该文件中。

Flush方法中的代码

如果(!\u havewriten序言)
{
_haveWrittenPremission=true;
ReadOnlySpan preamble=\u encoding.preamble;
如果(前导码长度>0)
{
_stream.Write(序言);
}
}


代码设置_haveWrittenPremission的值

//如果我们附加到一个已经有数据的流,不要 写
//序言。
如果(_stream.CanSeek&&u stream.Position>0)
{
_haveWrittenPremission=true;
}


注意,虽然UTF-8在技术上是允许的,但Unicode既不要求也不建议使用BOM(请参阅)。首先,它是无用的(与UTF-16不同)——UTF-8字节顺序由标准指定。另一方面,它会破坏文本处理。例如,如果XML序言之前有任何字符,许多XML解析器都会阻塞。您确定指定了UTF8吗?因为如果您不指定它,它仍然会写入UTF8,但没有Unicode标准5.0中的BOM:Unicode标准还指定使用初始字节顺序标记(BOM)来明确区分某些Unicode编码方案中的大端或小端数据。您解决了此问题吗?如果是这样,请标记正确答案或发布自己的答案以帮助他人。可能的编码重复是我们程序中的用户设置(通过TCP发送文本消息)。。。它是通过使用
enc=Encoding.GetEncoding(…)
进行简单解析来检索的。我找到的唯一解决方法是实际添加
if(enc是UTF8Encoding)enc=newutf8encoding(false)在它后面。虽然这是一个相当肮脏的补丁,但我看不到其他解决方法…@nyrguds这不是唯一的方法。您可以将编码的获取抽象到一个接口中,该接口给定一个参数,获取编码。然后将该接口的实现传递/注入到代码中。这样一来,所有的东西都变得非常干净。这就把同样的东西转移到了不同的类中。总的来说,我觉得GetEncoding以某种方式设法不使用默认构造函数是非常奇怪的。啊,好的