c#从字节数组中检测xml编码?

好吧,我有一个字节数组,我知道它在字节数组中的xml serized对象是否有任何方法可以从中获取编码?

我不打算去除它,但我将它保存在SQL服务器上的xml字段中…所以我需要将其转换为字符串?

你可以看看前40个字节的1个字节。 它们应该包含文档声明(假设它一个文档声明),它应该包含编码, 或者你可以假设它是UTF-8或UTF-16,这应该从你对部分的理解中显而易见。 (只需检查两种模式。)

实际上,你是否期望除了UTF-8或UTF-16之外你还能获得任何其他东西? 如果没有,您可以检查在这两个模式开始时获得的模式,如果不遵循任何模式,则抛出exception。 或者,如果您想再次尝试,可以尝试将文档解码为UTF-8,重新编码并查看是否返回相同的字节。 它并不理想,但它可能会起作用。

我确信有更严格的方法可以做到这一点,但它们可能很挑剔:)


1可能还不到这个。 我认为20个字符应该足够了,这是UTF-16中的40个字节。

类似于这个问题的解决方案可以通过在字节数组上使用Stream来解决这个问题 。 那么你就不必在字节级别进行操作。 像这样:

 Encoding encoding; using (var stream = new MemoryStream(bytes)) { using (var xmlreader = new XmlTextReader(stream)) { xmlreader.MoveToContent(); encoding = xmlreader.Encoding; } } 

前2或3个字节可以是字节顺序标记(BOM),它可以告诉您流是UTF-8,Unicode-LittleEndian还是Unicode-BigEndian。

UTF-8 BOM为0xEF 0xBB 0xBF Unicode-Bigendian为0xFE 0xFF Unicode-LittleEndiaon为0xFF 0xFE

如果这些都不存在,那么您可以使用ASCII来测试 (注意大多数现代XML生成都遵循标准,即xml声明之前没有空格)。

ASCII被用完直到?>所以你可以找到encoding =的存在并找到它的值。 如果编码不存在或声明不存在,那么您可以假设UTF-8。

W3C XML规范有一节介绍如何确定字节字符串的编码。

首先检查Unicode字节顺序标记

BOM只是另一个角色; 这是:

‘ZERO WIDTH NO-BREAK SPACE’(U + FEFF)

字符U + FEFF以及文件中的每个其他字符都使用适当的编码方案进行编码:

  • 00 00 FE FFUCS-4,大端机(1234订购)
  • FF FE 00 00UCS-4,小端机(4321订购)
  • 00 00 FF FEUCS-4,不寻常的八位字节顺序(2143)
  • FE FF 00 00UCS-4,不寻常的八位字节顺序(3412)
  • FE FF ## ##UTF-16,big-endian
  • FF FE ## ##UTF-16,little-endian
  • EF BB BFUTF-8

其中## ##可以是任何东西 – 除了两者都是零

因此,首先检查任何这些签名的初始字节。 如果找到其中一个,则返回该代码页标识符

 UInt32 GuessEncoding(byte[] XmlString) { if BytesEqual(XmlString, [00, 00, $fe, $ff]) return 12001; //"utf-32BE" - Unicode UTF-32, big endian byte order if BytesEqual(XmlString, [$ff, $fe, 00, 00]) return 1200; //"utf-32" - Unicode UTF-32, little endian byte order if BytesEqual(XmlString, [$fe, $ff, 00, 00]) throw new Exception("Nobody supports 2143 UCS-4"); if BytesEqual(XmlString, [$fe, $ff, 00, 00]) throw new Exception("Nobody supports 3412 UCS-4"); if BytesEqual(XmlString, [$fe, $ff]) { if (XmlString[2] <> 0) && (XmlString[3] <> 0) return 1201; //"unicodeFFFE" - Unicode UTF-16, big endian byte order } if BytesEqual(XmlString, [$ff, $fe]) { if (XmlString[2] <> 0) && (XmlString[3] <> 0) return 1200; //"utf-16" - Unicode UTF-16, little endian byte order } if BytesEqual(XmlString, [$ef, $bb, $bf]) return 65001; //"utf-8" - Unicode (UTF-8) 

或者寻找<?xml

如果XML文档没有字节顺序标记字符,那么您将继续查找每个XML文档必须具有的前五个字符:

了解这一点很有帮助

  • <是#x0000003C
  • ? 是#x0000003F

有了这个,我们就足以看到前四个字节了:

  • 00 00 00 3CUCS-4,大端机(1234订购)
  • 3C 00 00 00UCS-4,小端机(4321订购)
  • 00 00 3C 00UCS-4,不寻常的八位字节顺序(2143)
  • 00 3C 00 00UCS-4,不寻常的八位字节顺序(3412)
  • 00 3C 00 3FUTF-16,big-endian
  • 3C 00 3F 00UTF-16,little-endian
  • 3C 3F 78 6DUTF-8
  • 4C 6F A7 94某些EBCDIC的味道

所以我们可以在代码中添加更多内容:

  if BytesEqual(XmlString, [00, 00, 00, $3C]) return 12001; //"utf-32BE" - Unicode UTF-32, big endian byte order if BytesEqual(XmlString, [$3C, 00, 00, 00]) return 1200; //"utf-32" - Unicode UTF-32, little endian byte order if BytesEqual(XmlString, [00, 00, $3C, 00]) throw new Exception("Nobody supports 2143 UCS-4"); if BytesEqual(XmlString, [00, $3C, 00, 00]) throw new Exception("Nobody supports 3412 UCS-4"); if BytesEqual(XmlString, [00, $3C, 00, $3F]) return return 1201; //"unicodeFFFE" - Unicode UTF-16, big endian byte order if BytesEqual(XmlString, [$3C, 00, $3F, 00]) return 1200; //"utf-16" - Unicode UTF-16, little endian byte order if BytesEqual(XmlString, [$3C, $3F, $78, $6D]) return 65001; //"utf-8" - Unicode (UTF-8) if BytesEqual(XmlString, [$4C, $6F, $A7, $94]) { //Some variant of EBCDIC, eg: //20273 IBM273 IBM EBCDIC Germany //20277 IBM277 IBM EBCDIC Denmark-Norway //20278 IBM278 IBM EBCDIC Finland-Sweden //20280 IBM280 IBM EBCDIC Italy //20284 IBM284 IBM EBCDIC Latin America-Spain //20285 IBM285 IBM EBCDIC United Kingdom //20290 IBM290 IBM EBCDIC Japanese Katakana Extended //20297 IBM297 IBM EBCDIC France //20420 IBM420 IBM EBCDIC Arabic //20423 IBM423 IBM EBCDIC Greek //20424 IBM424 IBM EBCDIC Hebrew //20833 x-EBCDIC-KoreanExtended IBM EBCDIC Korean Extended //20838 IBM-Thai IBM EBCDIC Thai //20866 koi8-r Russian (KOI8-R); Cyrillic (KOI8-R) //20871 IBM871 IBM EBCDIC Icelandic //20880 IBM880 IBM EBCDIC Cyrillic Russian //20905 IBM905 IBM EBCDIC Turkish //20924 IBM00924 IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) throw new Exception("We don't support EBCDIC. Sorry"); } //Otherwise assume UTF-8, and fail to decode it anyway return 65001; //"utf-8" - Unicode (UTF-8) //Any code is in the public domain. No attribution required. }