c # Обнаруживает xml-кодировку из массива байтов?

StackOverflow https://stackoverflow.com/questions/581318

  •  06-09-2019
  •  | 
  •  

Вопрос

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

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

Это было полезно?

Решение

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

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

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


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

Другие советы

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

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 равна 0xEF 0xBB 0xBF Кодировка Unicode-Bigendian равна 0xFE 0xFF Кодировка Unicode-LittleEndiaon равна 0xFF 0xFE

Если ничего из этого нет, то вы можете использовать ASCII для проверки <?xml (обратите внимание, что большинство современных генераций XML придерживаются стандарта, согласно которому перед объявлением xml не должно быть пробелов).

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

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

Сначала проверьте наличие Метки Порядка байтов в Юникоде

Спецификация - это просто еще один символ;это тот самый:

'ПРОБЕЛ БЕЗ РАЗРЫВОВ НУЛЕВОЙ ШИРИНЫ' (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, кодировка с большим окончанием
  • FF FE ## ##: UTF-16, младший порядковый номер
  • 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

Если в XML-документе нет знака порядка байтов, то вы переходите к поиску первых пяти символов, которые должны быть в каждом XML-документе:

<?xml

Полезно знать, что

  • < является #x0000003C
  • ? является #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, кодировка с большим окончанием
  • 3C 00 3F 00: UTF-16, младший порядковый номер
  • 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.
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top