.net - net - Encoding.UTF8.GetString no tiene en cuenta el Preámbulo/BOM
text encoding vb net (3)
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 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"));
}
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 codificación 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