2011-03-10 110 views
44

我遇到StreamWriter和Byte Order Marks的问题。该文档似乎指出,Encoding.UTF8编码启用了字节顺序标记,但是在写入文件时有些标记有标记,而其他标记没有。StreamWriter和UTF-8字节顺序标记

我通过以下方式创建流作家:

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

什么可能会发生,将不胜感激任何想法。

+1

请注意,尽管技术上允许使用UTF-8,但Unicode不需要或不推荐使用BOM(请参见[参考资料](http://www.unicode.org/versions/Unicode5.0.0/ch02.pdf ))。首先,它是无用的(与UTF-16不同) - UTF-8字节顺序由标准规定。另外,它可能会搞砸文本处理。例如,如果在XML prolog之前有任何字符,许多XML解析器将会窒息。 – 2011-03-10 21:36:20

+1

你确定你确定你在指定UTF8吗?因为如果你没有指定它,它仍然会写一个UTF8,但是没有BOM – xanatos 2011-03-10 21:38:00

+0

来自Unicode标准5.0:* Unicode标准还指定 使用初始字节顺序标记(BOM)来明确区分大小写字母,在一些Unicode编码方案中,可以使用endian或little endian数据。* – 2012-09-06 21:18:15

回答

5

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

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

我前段时间处于类似的情况。我结束了使用Stream.Write方法替代的StreamWriter和写的Encoding.GetPreamble()结果写我见过的构造不添加UTF-8 BOM是Encoding.GetBytes(stringToWrite)

11

唯一的时间之前,如果流不是在位置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。

0

请问您能否展示一个不生产它的情况?我能找到的序言不存在的唯一情况是,什么都没有写给作者(吉姆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) 
    ); 
2

看来,如果该文件已经存在,并没有包含BOM,那么它将不会被覆盖时包含BOM,在StreamWriter的保留BOM换言之(或它的缺席)覆盖文件时。

61

正如有人指出,已经没有编码参数调用的伎俩。 但是,如果你想成为明确的,试试这个:

using (var sw = new StreamWriter("text.txt", new UTF8Encoding(false))) 

的关键是构建而不是使用Encoding.UTF8Encoding新的UTF8Encoding(假)。这是为了控制是否应该添加BOM。

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

13

此问题是由于您在Encoding class上使用静态UTF8 property

GetPreamble method被称为由UTF8属性返回的Encoding类的实例,则返回字节顺序标记(三个字符的字节阵列)和之前的任何其他内容被写入到写入流流(假设新流)。

您可以自己创建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将被写入到基础流。

+0

编码是我们的程序(通过TCP发送文本消息)中的用户设置...它通过一个简单的解析与'enc = Encoding.GetEncoding(...)'进行检索。我发现的唯一方法是在它后面添加“if(enc是UTF8Encoding)enc = new UTF8Encoding(false);”。虽然很肮脏的修复,但我看不到其他方式来解决它... – Nyerguds 2013-04-11 11:41:38

+0

@Nyerguds这不是唯一的方法。您可以将获取的编码抽象为给定参数的接口,获取编码。然后,您将该接口的实现传递给您的代码。然后它使一切都相当干净。 – casperOne 2013-04-11 11:45:52

+0

这只是将相同的东西移动到不同的类。总的来说,我发现GetEncoding以某种方式管理不使用默认构造函数是非常奇怪的。呃,好吧。 – Nyerguds 2013-04-11 11:53:31

9

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

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

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Привет"); 
     } 
    } 
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(); 
} 
+1

我主要发现了'新的UTF8Encoding(true)'构造函数有用。 – 2016-06-04 21:26:20