C# StreamWriter和UTF-8字节顺序标记
我对StreamWriter和字节顺序标记有问题。文档似乎说明Encoding.UTF8编码已启用字节顺序标记,但在写入文件时,有些文件具有标记,而另一些文件没有 我以以下方式创建流编写器: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创建流写器,考
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以某种方式设法不使用默认构造函数是非常奇怪的。啊,好的