2012-07-28 11 views
22

In .NET versuche ich Encoding.UTF8.GetString Methode, die ein Byte-Array verwendet und konvertiert es in eine string.Encoding.UTF8.GetString berücksichtigt nicht die Präambel/BOM

Es sieht so aus, als ob diese Methode die BOM (Byte Order Mark) ignoriert, die Teil einer legitimen Binärdarstellung einer UTF8-Zeichenfolge sein könnte, und sie als Zeichen interpretiert.

Ich weiß, dass ich eine TextReader verwenden kann, um die Stückliste nach Bedarf zu verdauen, aber ich dachte, dass die GetString-Methode eine Art Makro sein sollte, das unseren Code kürzer macht.

Fehle ich etwas? Ist das so absichtlich?

Hier ist eine Reproduktion Code:

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")); 
} 

Antwort

18

Es sieht aus wie diese Methode die BOM (Byte Order Mark) ignoriert, die ein Teil eines legitimen Binärdarstellung einer UTF8-String sein könnte, und es dauert als ein Charakter.

Es sieht nicht so aus, als ob es es überhaupt "ignoriert" - es wandelt es getreulich in den BOM-Charakter um. So ist es schließlich.

Wenn Sie Ihre Code machen ignorieren die BOM in eine beliebige Zeichenfolge es konvertiert, das ist bis zu Ihnen ... oder StreamReader verwenden zu tun.

Beachten Sie, dass, wenn Sie entweder Verwendung Encoding.GetBytes von Encoding.GetStringoder Verwendung gefolgt StreamWriter von StreamReader gefolgt werden beide Formen entweder produzieren dann die BOM schlucken oder nicht produzieren. Es ist nur, wenn Sie eine StreamWriter (die Encoding.GetPreamble verwendet) mit einem direkten Encoding.GetString Anruf mischen, dass Sie mit dem "Extra" -Zeichen enden. (Danke!)

+0

ich sehe. Danke für die Klarstellung! –

+5

@RonKlein Zusätzlich können Sie 'restore2 = restore2.TrimStart ('\ uFEFF')' sagen, um führende BOM-Zeichen zu entfernen. Ich habe auch einmal gefragt, warum '(neue UTF8Encoding (true)). GetBytes (" abc ")' und '(neue UTF8Encoding (false)). GetBytes (" abc ")' produzieren die gleiche Ausgabe, aber wie Sie wahrscheinlich "GetBytes" geht nicht davon aus, dass Sie sich am Anfang einer Datei befinden. Daher wird nie "GetPreamble" verwendet. Sie müssen 'GetPreamble' explizit angeben oder die Präambel explizit überspringen, wenn Sie' GetBytes' oder 'GetString' verwenden. –

7

Basierend auf der Antwort von Jon Skeet, das ist, wie ich es gerade getan:

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

Beachten Sie, dass dies wahrscheinlich nur dann zuverlässig funktionieren, wenn es eine Stückliste im Byte-Array ist du liest gerade vor. Wenn nicht, können Sie in another StreamReader constructor overload suchen, die einen Encoding-Parameter benötigt, damit Sie ihm sagen können, was das Byte-Array enthält.

+0

Ich denke, dass Sie stattdessen [diese Konstruktorüberladung] (https://msdn.microsoft.com/en-us/library/ms143457 (v = vs.110) .aspx) möchten, die Sie angeben können, ob es nach a suchen soll BOM, um die Kodierung herauszufinden. – drzaus

0

Ich weiß, ich bin ein bisschen spät, um die Partei, aber hier ist der Code, den ich (fühlen Sie sich frei zu C# anzupassen) verwenden, wenn Sie benötigen:

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 
Verwandte Themen