.net Encoding.UTF8.GetString不';t考虑序言/BOM
在.NET中,我尝试使用.net Encoding.UTF8.GetString不';t考虑序言/BOM,.net,unicode,character-encoding,byte-order-mark,.net,Unicode,Character Encoding,Byte Order Mark,在.NET中,我尝试使用Encoding.UTF8.GetString方法,该方法获取字节数组并将其转换为字符串 此方法似乎忽略了,它可能是UTF8字符串的合法二进制表示形式的一部分,并将其作为字符 我知道我可以使用TextReader根据需要对BOM进行摘要,但我认为GetString方法应该是某种宏,可以使代码更短 我错过什么了吗?这是故意的吗 这是一个复制代码: static void Main(string[] args) { string s1 = "abc"; byt
Encoding.UTF8.GetString
方法,该方法获取字节数组并将其转换为字符串
此方法似乎忽略了,它可能是UTF8字符串的合法二进制表示形式的一部分,并将其作为字符
我知道我可以使用TextReader
根据需要对BOM进行摘要,但我认为GetString方法应该是某种宏,可以使代码更短
我错过什么了吗?这是故意的吗
这是一个复制代码:
static void Main(string[] args)
{
string s1 = "abc";
byte[] abcWithBom;
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms, new UTF8Encoding(true)))
{
sw.Write(s1);
sw.Flush();
abcWithBom = ms.ToArray();
Console.WriteLine(FormatArray(abcWithBom)); // ef, bb, bf, 61, 62, 63
}
byte[] abcWithoutBom;
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms, new UTF8Encoding(false)))
{
sw.Write(s1);
sw.Flush();
abcWithoutBom = ms.ToArray();
Console.WriteLine(FormatArray(abcWithoutBom)); // 61, 62, 63
}
var restore1 = Encoding.UTF8.GetString(abcWithoutBom);
Console.WriteLine(restore1.Length); // 3
Console.WriteLine(restore1); // abc
var restore2 = Encoding.UTF8.GetString(abcWithBom);
Console.WriteLine(restore2.Length); // 4 (!)
Console.WriteLine(restore2); // ?abc
}
private static string FormatArray(byte[] bytes1)
{
return string.Join(", ", from b in bytes1 select b.ToString("x"));
}
此方法似乎忽略了BOM(字节顺序标记),它可能是UTF8字符串的合法二进制表示形式的一部分,并将其作为字符
它看起来一点也不“忽略”它——它忠实地将其转换为BOM字符。毕竟是这样的
如果你想让你的代码忽略它转换的任何字符串中的BOM表,那就由你来做。。。或者使用StreamReader
请注意,如果使用Encoding.GetBytes
后跟Encoding.GetString
或使用StreamWriter
后跟StreamReader
,这两种表单都将生成然后吞并BOM表或不生成BOM表。只有当你混合使用StreamWriter
(它使用编码.GetPreamble
)和直接编码.GetString
调用时,你才会得到“额外”字符。根据Jon Skeet的回答(谢谢!),我就是这样做的:
var memoryStream = new MemoryStream(byteArray);
var s = new StreamReader(memoryStream).ReadToEnd();
请注意,只有在您正在读取的字节数组中存在BOM表时,此功能才可能可靠地工作。如果没有,您可能需要查看哪个接受了编码参数,这样您就可以告诉它字节数组包含什么。我知道我参加聚会有点晚了,但如果您需要,下面是我正在使用的代码(请随意适应C#):
公共函数序列化(您的XmlClass)(ByVal obj作为您的XmlClass,
可选的ByVal omitXMLDeclaration为Boolean=True,
可选的ByVal省略XMLNamespace作为Boolean=True)作为字符串
Dim序列化程序作为新的XmlSerializer(obj.GetType)
将memStream用作新的MemoryStream()
将设置设置设置为新的XmlWriterSettings(){
.Encoding=Encoding.UTF8,
.Indent=True,
.omitXMLDeclaration=omitXMLDeclaration}
使用writer作为XmlWriter=XmlWriter.Create(memStream,设置)
Dim xns作为新的XmlSerializerNamespaces
如果是(省略XMLNamespace),那么xns.Add(“,”)
serializer.Serialize(writer、obj、xns)
终端使用
返回Encoding.UTF8.GetString(memStream.ToArray())
终端使用
端函数
公共函数反序列化(您的XmlClass)(ByVal obj作为您的XmlClass,ByVal xml作为字符串)作为您的XmlClass
将结果设置为您的XmlClass
Dim序列化程序作为新的XmlSerializer(GetType(YourXMLClass))
将memStream用作新的MemoryStream()
Dim bytes As Byte()=Encoding.UTF8.GetBytes(xml.ToArray)
memStream.Write(字节,0,字节.计数)
memStream.Seek(0,SeekOrigin.Begin)
将reader用作XmlReader=XmlReader.Create(memStream)
结果=DirectCast(序列化程序.反序列化(读取器),YourXMLClass)
终端使用
终端使用
返回结果
端函数
对于那些不想使用streams的人,我发现了一个使用Linq的非常简单的解决方案:
public static string GetStringExcludeBOMPreamble(this Encoding encoding, byte[] bytes)
{
var preamble = encoding.GetPreamble();
if (preamble?.Length > 0 && bytes.Length >= preamble.Length && bytes.Take(preamble.Length).SequenceEqual(preamble))
{
return encoding.GetString(bytes, preamble.Length, bytes.Length - preamble.Length);
}
else
{
return encoding.GetString(bytes);
}
}
@RonKlein另外,您可以说restore2=restore2.TrimStart('\uFEFF')
来删除BOM表的前导字符。我也曾经想知道为什么(新的UTF8Encoding(true)).GetBytes(“abc”)和(新的UTF8Encoding(false)).GetBytes(“abc”)
会产生相同的输出,但正如您现在可能知道的那样,GetBytes
并不假定您位于文件的开头,因此它从不使用GetPreamble
。如果使用GetBytes
或GetString
,则必须显式地GetPreamble
,或者显式地跳过preamble。我认为您可能需要使用它来指定是否应该查找BOM以确定编码。