Encoding.UTF8.GetString no tiene en cuenta el Preámbulo / BOM

En .NET, estoy tratando de usar el método Encoding.UTF8.GetString , que toma una matriz de bytes y la convierte en una string .

Parece que este método ignora la BOM (Marca de orden de bytes) , que podría ser parte de una representación binaria legítima de una cadena UTF8, y la toma como un carácter.

Sé que puedo usar un TextReader para digerir la lista de materiales según sea necesario, pero pensé que el método GetString debería ser una especie de macro que hace que nuestro código sea más corto.

¿Me estoy perdiendo de algo? ¿Es esto tan intencionalmente?

Aquí hay un código de reproducción:

 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 restre1 = Encoding.UTF8.GetString(abcWithoutBom); Console.WriteLine(restre1.Length); // 3 Console.WriteLine(restre1); // abc var restre2 = Encoding.UTF8.GetString(abcWithBom); Console.WriteLine(restre2.Length); // 4 (!) Console.WriteLine(restre2); // ?abc } private static string FormatArray(byte[] bytes1) { return string.Join(", ", from b in bytes1 select b.ToString("x")); } 

Parece que este método ignora la BOM (Marca de orden de bytes), que podría ser parte de una representación binaria legítima de una cadena UTF8, y la toma como un carácter.

No parece que lo “ignore” en absoluto, lo convierte fielmente en el personaje BOM. Eso es lo que es, después de todo.

Si desea hacer que su código ignore la lista de materiales en cualquier cadena que convierta, eso depende de usted … o use StreamReader .

Tenga en cuenta que si utiliza Encoding.GetBytes seguido de Encoding.GetString o usa StreamWriter seguido de StreamReader , ambos formularios producirán y luego tragarán o no producirán la lista de materiales. Solo cuando mezcla con StreamWriter (que utiliza Encoding.GetPreamble ) con una llamada directa Encoding.GetString , termina con el carácter “extra”.

Basado en la respuesta de Jon Skeet (¡gracias!), Así es como lo hice:

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

Tenga en cuenta que esto probablemente solo funcionará de manera confiable si hay una lista de materiales en la matriz de bytes desde la que está leyendo. De lo contrario, es posible que desee examinar otra sobrecarga del constructor de StreamReader que toma un parámetro de encoding para que pueda indicarle qué contiene la matriz de bytes.

Sé que llego un poco tarde a la fiesta, pero aquí está el código que estoy usando (siéntase libre de adaptarse a C #) si necesita:

  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