Encodage.UTF8.GetString ne tient pas compte du préambule/BOM

.NET, je suis en train d'utiliser Encoding.UTF8.GetString méthode, qui prend un tableau d'octets, et la convertit en un string.

on dirait que cette méthode ignore le BOM (Byte Order Mark), qui pourrait être une partie d'une représentation binaire légitime D'une chaîne UTF8, et le prend comme un caractère.

je sais que je peux utiliser un TextReader pour digérer le BOM comme nécessaire, mais j'ai pensé que la méthode GetString devrait être une sorte de macro qui fait notre code plus court.

Est-ce que je rate quelque chose? Est-ce comme intentionnellement?

voici un code de reproduction:

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"));
}
22
demandé sur Charles 2012-07-28 17:16:38

3 réponses

il semble que cette méthode ignore le BOM (Byte Order Mark), qui pourrait faire partie d'une représentation binaire légitime D'une chaîne UTF8, et le prend comme un caractère.

on ne dirait pas qu'il "l'ignore" du tout - il le convertit fidèlement au caractère BOM. C'est ce qu'il est, après tout.

Si vous voulez faire code ignore le BOM dans n'importe quelle chaîne qu'il convertit, c'est à vous de le faire... ou de l'utilisation StreamReader.

Notez que si vous utiliser Encoding.GetBytes suivi de Encoding.GetString ou utiliser StreamWriter suivi de StreamReader, les deux formes produiront puis avaleront ou ne produiront pas le BOM. C'est seulement quand vous mélangez en utilisant un StreamWriter (qui utilise Encoding.GetPreamble) avec un lien direct Encoding.GetString appelez que vous finissez avec le caractère" extra".

19
répondu Jon Skeet 2012-07-28 13:47:22

basé sur la réponse de Jon Skeet (merci!), c'est la façon dont je l'ai fait:

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

notez que cela ne fonctionnera probablement de manière fiable que s'il y a un BOM dans le tableau des octets que vous lisez. Si non, vous pourriez vouloir regarder dans un autre StreamReader surcharge du constructeur qui prend un paramètre D'encodage pour que vous puissiez lui dire ce que contient le tableau des octets.

8
répondu Per Lundberg 2017-06-13 15:08:22

je sais que je suis un peu en retard à la fête, mais voici le code, je suis en utilisant (n'hésitez pas à l'adapter pour C#) si vous avez besoin de:

 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
0
répondu Denis 2015-04-01 18:45:28