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