27

在 .NET 中,我正在尝试使用Encoding.UTF8.GetString方法,该方法采用字节数组并将其转换为string.

看起来此方法忽略了BOM(字节顺序标记),它可能是 UTF8 字符串的合法二进制表示的一部分,并将其视为字符。

我知道我可以TextReader根据需要使用 a 来消化 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"));
}
4

4 回答 4

30

看起来此方法忽略了 BOM(字节顺序标记),它可能是 UTF8 字符串的合法二进制表示的一部分,并将其视为字符。

它看起来根本没有“忽略”它 - 它忠实地将其转换为 BOM 字符。毕竟就是这样。

如果你想让你的代码忽略它转换的任何字符串中的 BOM,这取决于你做...或使用StreamReader.

请注意,如果您使用Encoding.GetBytes后跟Encoding.GetString 使用StreamWriter后跟StreamReader,两种形式都将生成然后吞下或不生成 BOM 。只有当您将 a StreamWriter(使用Encoding.GetPreamble)与直接Encoding.GetString调用混合使用时,您才会得到“额外”字符。

于 2012-07-28T13:47:22.850 回答
10

根据 Jon Skeet 的回答(谢谢!),我就是这样做的:

var memoryStream = new MemoryStream(byteArray);
var s = new StreamReader(memoryStream).ReadToEnd();

请注意,这可能只有在您正在读取的字节数组中有 BOM 时才能可靠地工作。如果没有,您可能需要查看另一个 StreamReader 构造函数重载,它采用 Encoding 参数,以便您可以告诉它字节数组包含什么。

于 2013-11-20T15:36:35.240 回答
5

对于那些不想使用流的人,我找到了一个使用 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);
    }
}
于 2018-12-12T10:32:50.220 回答
0

我知道我参加聚会有点晚了,但是如果您需要,这是我正在使用的代码(可以随意适应 C#):

Public Function Serialize(Of YourXMLClass)(ByVal obj As YourXMLClass,
                                                      Optional ByVal omitXMLDeclaration As Boolean = True,
                                                      Optional ByVal omitXMLNamespace As Boolean = True) As String

    Dim serializer As New XmlSerializer(obj.GetType)
    Using memStream As New MemoryStream()
        Dim settings As New XmlWriterSettings() With {
                    .Encoding = Encoding.UTF8,
                    .Indent = True,
                    .omitXMLDeclaration = omitXMLDeclaration}

        Using writer As XmlWriter = XmlWriter.Create(memStream, settings)
            Dim xns As New XmlSerializerNamespaces
            If (omitXMLNamespace) Then xns.Add("", "")
            serializer.Serialize(writer, obj, xns)
        End Using

        Return Encoding.UTF8.GetString(memStream.ToArray())
    End Using
End Function

Public Function Deserialize(Of YourXMLClass)(ByVal obj As YourXMLClass, ByVal xml As String) As YourXMLClass
    Dim result As YourXMLClass
    Dim serializer As New XmlSerializer(GetType(YourXMLClass))

    Using memStream As New MemoryStream()
        Dim bytes As Byte() = Encoding.UTF8.GetBytes(xml.ToArray)
        memStream.Write(bytes, 0, bytes.Count)
        memStream.Seek(0, SeekOrigin.Begin)

        Using reader As XmlReader = XmlReader.Create(memStream)
            result = DirectCast(serializer.Deserialize(reader), YourXMLClass)
        End Using

    End Using
    Return result
End Function
于 2015-04-01T18:45:28.433 回答