2015-10-21 2 views
3

В RFC4627 описан способ идентификации кодировки Unicode, когда спецификация спецификации отсутствует. Это полагалось на первые 2 символа в тексте JSON, чтобы всегда быть символами ASCII. Но в RFC7159 спецификация определяет текст JSON как «ws value ws»; подразумевая, что одно строковое значение также будет действительным. Таким образом, первым символом будет вводная цитата, но затем любой символ Юникода, разрешенный в строке, может следовать. Учитывая, что RFC7159 также препятствует использованию спецификации; и больше не описывает процедуру обнаружения кодировки из первых 4 октетов (байтов), как ее можно обнаружить? UTF-32 должен работать корректно, как описано в RFC4627, потому что первый символ имеет четыре байта и должен быть ASCII, но как насчет UTF-16? Второй (2-байтовый) символ может не содержать нулевой байт, чтобы помочь определить правильную кодировку.Точное определение кодирования JSON

+0

Интересный вопрос. Разумеется, определение схемы Unicode является более сложной задачей. Например, один байт, значение ASCII которого является цифрой, также является допустимым JSON: его одноразрядное число в UTF-8. Как насчет предложения решения, и мы можем обсудить ваше решение? – CouchDeveloper

+0

Боюсь, у меня нет предложения, которое будет надежным. Очевидно, сначала нужно проверить спецификацию на всякий случай. Следующая проверка для UTF-32 - если первые 3 байта равны нулю, то это UTF-32BE, иначе, если 3 байта после первого - это нули, это UTF-32LE. До сих пор мы должны были опираться на тест, но теперь возникает проблема. Чтобы проверить UTF-16, нам все равно придется искать 4 байта. Если бы мы могли предположить, что первые 2 символа всегда ASCII, то мы проверяем, являются ли первый и третий байты нулями для UTF-16BE, а второй и четвертый для UTF-16LE. Но если второй символ может быть не-ASCII, что тогда? –

+0

Помогло бы, если бы я дал вам головной убор с реализацией на C++ для обнаружения кодировки? Насколько я знаю, вам не требуется обнаружение спецификации, но я тоже реализовал его на C++. – CouchDeveloper

ответ

1

После взглянуть на реализации, которые я сделал пару лет назад, я могу сказать, что это можно однозначно обнаружить данную схему Unicode от всего один характера, учитывая следующие допущения:

  • вход должен быть Unicode
  • первый символ должен быть ASCII
  • Там не должно быть никакого Bom

Co nsider this:

Предполагая, что первый символ является "[" (0x5B) - ASCII. Тогда мы можем получить эти шаблоны байт:

UTF_32LE: 5B 00 00 00 
UTF_32BE: 00 00 00 5B 
UTF_16LE: 5B 00 xx xx 
UTF_16BE: 00 5B xx xx 
UTF_8:  5B xx xx xx 

, где «хх» либо EOF или любой другой байт.

Следует также отметить, что в соответствии с RFC7159 самым коротким действительным JSON может быть только один символ. То есть, это может быть возможно 1, 2 или 4 байта - в зависимости от схемы Unicode.

Так что, если это помогает, вот реализация в C++:

namespace json { 

    // 
    // Detect Encoding 
    // 
    // Tries to determine the Unicode encoding of the input starting at 
    // first. A BOM shall not be present (you might check with function 
    // json::unicode::detect_bom() whether there is a BOM, in which case 
    // you don't need to call this function when a BOM is present). 
    // 
    // Return values: 
    // 
    // json::unicode::UNICODE_ENCODING_UTF_8 
    // json::unicode::UNICODE_ENCODING_UTF_16LE 
    // json::unicode::UNICODE_ENCODING_UTF_16BE 
    // json::unicode::UNICODE_ENCODING_UTF_32LE 
    // json::unicode::UNICODE_ENCODING_UTF_32BE 
    // 
    // -1:  unexpected EOF 
    // -2:  unknown encoding 
    // 
    // Note: 
    // detect_encoding() requires to read ahead a few bytes in order to deter- 
    // mine the encoding. In case of InputIterators, this has the consequences 
    // that these iterators cannot be reused, for example for a parser. 
    // Usually, this requires to reset the istreambuff, that is using the 
    // member functions pubseekpos() or pupseekoff() in order to reset the get 
    // pointer of the stream buffer to its initial position. 
    // However, certain istreambuf implementations may not be able to set the  
    // stream pos at arbitrary positions. In this case, this method cannot be 
    // used and other edjucated guesses to determine the encoding may be 
    // needed. 

    template <typename Iterator>  
    inline int 
    detect_encoding(Iterator first, Iterator last) 
    { 
     // Assuming the input is Unicode! 
     // Assuming first character is ASCII! 

     // The first character must be an ASCII character, say a "[" (0x5B) 

     // UTF_32LE: 5B 00 00 00 
     // UTF_32BE: 00 00 00 5B 
     // UTF_16LE: 5B 00 xx xx 
     // UTF_16BE: 00 5B xx xx 
     // UTF_8:  5B xx xx xx 

     uint32_t c = 0xFFFFFF00; 

     while (first != last) { 
      uint32_t ascii; 
      if (static_cast<uint8_t>(*first) == 0) 
       ascii = 0; // zero byte 
      else if (static_cast<uint8_t>(*first) < 0x80) 
       ascii = 0x01; // ascii byte 
      else if (*first == EOF) 
       break; 
      else 
       ascii = 0x02; // non-ascii byte, that is a lead or trail byte 
      c = c << 8 | ascii; 
      switch (c) { 
        // reading first byte 
       case 0xFFFF0000: // first byte was 0 
       case 0xFFFF0001: // first byte was ASCII 
        ++first; 
        continue; 
       case 0xFFFF0002: 
        return -2; // this is bogus 

        // reading second byte 
       case 0xFF000000: // 00 00 
        ++first; 
        continue; 
       case 0xFF000001: // 00 01 
        return json::unicode::UNICODE_ENCODING_UTF_16BE; 
       case 0xFF000100: // 01 00 
        ++first; 
        continue; 
       case 0xFF000101: // 01 01 
        return json::unicode::UNICODE_ENCODING_UTF_8; 

        // reading third byte:  
       case 0x00000000: // 00 00 00 
       case 0x00010000: // 01 00 00 
        ++first; 
        continue;      
        //case 0x00000001: // 00 00 01 bogus 
        //case 0x00000100: // 00 01 00 na 
        //case 0x00000101: // 00 01 01 na 
       case 0x00010001: // 01 00 01 
        return json::unicode::UNICODE_ENCODING_UTF_16LE; 

        // reading fourth byte  
       case 0x01000000: 
        return json::unicode::UNICODE_ENCODING_UTF_32LE; 
       case 0x00000001: 
        return json::unicode::UNICODE_ENCODING_UTF_32BE; 

       default: 
        return -2; // could not determine encoding, that is, 
           // assuming the first byte is an ASCII. 
      } // switch 
     } // while 

     // premature EOF 
     return -1; 
    } 
} 
+0

Спасибо за код. Мой исходный код следовал процедуре, описанной в RFC4627, который смотрит на первые 4 байта следующим образом: 00 00 00 хх UTF-32BE, 00 хх 00 хх UTF-16BE, хх 00 00 00 UTF-32LE, хх 00 xx 00 UTF-16LE, xx xx xx xx UTF-8. С RFC 7159, допускающим однострочное значение, можно использовать что-то вроде 00 xx xx 00 или xx 00 00 xx, когда используется UTF-16. Так что я просто изменил мою следующим образом: 00 00 00 хх UTF-32BE, хх 00 00 00 UTF-32LE, 00 хх UTF-16BE, хх 00 UTF-16LE, все остальное UTF-8. Насколько я вижу, ваш код должен работать так, как есть. –

Смежные вопросы