c # Определить кодировку XML из байтового массива? - PullRequest
6 голосов
/ 24 февраля 2009

Ну, у меня есть байтовый массив, и я знаю, что это XML-серилизированный объект в байтовом массиве. Есть ли способ получить кодировку из него?

Я не собираюсь десерилизовать его, но сохраняю в поле xml на сервере sql ... поэтому мне нужно преобразовать его в строку?

Ответы [ 4 ]

14 голосов
/ 12 марта 2009

Решение, аналогичное , этот вопрос может решить эту проблему с помощью потока поверх байтового массива. Тогда вам не придется возиться на уровне байтов. Как это:

Encoding encoding;
using (var stream = new MemoryStream(bytes))
{
    using (var xmlreader = new XmlTextReader(stream))
    {
        xmlreader.MoveToContent();
        encoding = xmlreader.Encoding;
    }
}
7 голосов
/ 24 февраля 2009

Первые 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 придерживается стандарта, согласно которому ни один пробел не может предшествовать объявлению xml).

ASCII используется вплоть до ?>, поэтому вы можете найти наличие кодировки = и найти ее значение. Если кодировка отсутствует или <?xml Declare отсутствует, то вы можете принять UTF-8.

7 голосов
/ 24 февраля 2009

Вы можете посмотреть на первые 40 байтов байта 1 . Они должны содержать декларацию документа (при условии, что имеет декларацию документа), которая должна содержать либо кодировку , либо , вы можете предположить, что это UTF-8 или UTF-16 Это должно быть очевидно из того, как вы поняли часть <?xml. (Просто проверьте оба шаблона.)

Реально, ожидаете ли вы, что когда-нибудь получите что-то кроме UTF-8 или UTF-16? Если нет, вы можете проверить наличие шаблонов, которые вы получаете в начале обоих, и выдать исключение, если оно не следует ни одному из шаблонов. В качестве альтернативы, если вы хотите сделать еще одну попытку, вы всегда можете попытаться декодировать документ как UTF-8, перекодировать его и посмотреть, получите ли вы те же байты обратно. Это не идеально, но это может сработать.

Я уверен, что есть более строгие способы сделать это, но они, вероятно, будут привередливыми:)


1 Вполне возможно, меньше, чем это. Я считаю, что 20 символов должно быть достаточно, что составляет 40 байт в UTF-16.

6 голосов
/ 23 февраля 2016

В спецификации W3C XML есть раздел о том, как определить кодировку байтовой строки.

Первая проверка на метку порядка байтов Unicode

Спецификация - это просто другой персонаж; это:

'НУЛЬ ПЕРЕКЛЮЧЕНИЯ С НУЛЕМ' (U + FEFF)

Символ U + FEFF вместе со всеми другими символами в файле кодируется с использованием соответствующей схемы кодирования:

  • 00 00 FE FF: UCS-4, машина с прямым порядком байтов (порядок 1234)
  • FF FE 00 00: UCS-4, машина с прямым порядком байтов (порядок 4321)
  • 00 00 FF FE: UCS-4, необычный порядок октетов (2143)
  • FE FF 00 00: UCS-4, необычный порядок октетов (3412)
  • FE FF ## ##: UTF-16, big-endian
  • FF FE ## ##: UTF-16, little-endian
  • EF BB BF: UTF-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 </h2> Если документ XML не имеет символа метки байтового порядка, вы переходите к поиску первых пяти символов, которые должен иметь каждый документ XML: <?xml Полезно знать, что < is # x0000003C ? is # x0000003F С этого нам достаточно взглянуть на первые четыре байта: 00 00 00 3C: UCS-4, машина с прямым порядком байтов (порядок 1234) 3C 00 00 00: UCS-4, машина с прямым порядком байтов (порядок 4321) 00 00 3C 00: UCS-4, необычный порядок октетов (2143) 00 3C 00 00: UCS-4, необычный порядок октетов (3412) 00 3C 00 3F: UTF-16, big-endian 3C 00 3F 00: UTF-16, little-endian 3C 3F 78 6D: UTF-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, e.g.: //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. }

...