2012-04-21 11 views
8

Introducción

std::string text = "á"; 

"a" es el carácter de dos bytes (suponiendo una codificación UTF-8). Así
siguiente imprime la línea 2.impulso :: :: property_tree json_parser y de dos bytes caracteres anchos

std::cout << text.size() << "\n"; 

Pero todavía std::cout texto se imprime correctamente.

std::cout << text << "\n"; 

Mi problema

Paso text a boost::property_tree::ptree y luego a write_json

boost::property_tree::ptree root; 
root.put<std::string>("text", text); 

std::stringstream ss; 
boost::property_tree::json_parser::write_json(ss, root); 
std::cout << ss.str() << "\n"; 

El resultado es

{ 
    "text": "\u00C3\u00A1" 
} 

texto es igual a "¡" que es diferente que "á".

¿Es posible solucionar este problema sin cambiar a std::wstring? ¿Es posible que cambiar la biblioteca (boost::property_tree::ptree) pueda resolver este problema?

Respuesta

10

Encontré algunas soluciones. En general, debe especificar la plantilla boost::property_tree::json_parser::create_escapes para [Ch=Char], para proporcionar su "escape libre de errores de ocasión especial".

El estándar JSON supone que todas las cadenas están codificadas en UTF-16 con el escape "\ uXXXX", pero algunas bibliotecas admiten la codificación UTF-8 con el escape "\ xXX". Si el archivo JSON se puede codificar en UTF-8, puede pasar todo el carácter a más de 0x7F, que fue diseñado para la función original.

Pongo este código antes de usar boost::property_tree::json_parser::write_json. Viene de boost_1_49_0/boost/property_tree/detail/json_parser_write.hpp:

namespace boost { namespace property_tree { namespace json_parser 
{ 
    // Create necessary escape sequences from illegal characters 
    template<> 
    std::basic_string<char> create_escapes(const std::basic_string<char> &s) 
    { 
     std::basic_string<char> result; 
     std::basic_string<char>::const_iterator b = s.begin(); 
     std::basic_string<char>::const_iterator e = s.end(); 
     while (b != e) 
     { 
      // This assumes an ASCII superset. But so does everything in PTree. 
      // We escape everything outside ASCII, because this code can't 
      // handle high unicode characters. 
      if (*b == 0x20 || *b == 0x21 || (*b >= 0x23 && *b <= 0x2E) || 
       (*b >= 0x30 && *b <= 0x5B) || (*b >= 0x5D && *b <= 0xFF) //it fails here because char are signed 
       || (*b >= -0x80 && *b < 0)) // this will pass UTF-8 signed chars 
       result += *b; 
      else if (*b == char('\b')) result += char('\\'), result += char('b'); 
      else if (*b == char('\f')) result += char('\\'), result += char('f'); 
      else if (*b == char('\n')) result += char('\\'), result += char('n'); 
      else if (*b == char('\r')) result += char('\\'), result += char('r'); 
      else if (*b == char('/')) result += char('\\'), result += char('/'); 
      else if (*b == char('"')) result += char('\\'), result += char('"'); 
      else if (*b == char('\\')) result += char('\\'), result += char('\\'); 
      else 
      { 
       const char *hexdigits = "ABCDEF"; 
       typedef make_unsigned<char>::type UCh; 
       unsigned long u = (std::min)(static_cast<unsigned long>(
               static_cast<UCh>(*b)), 
              0xFFFFul); 
       int d1 = u/4096; u -= d1 * 4096; 
       int d2 = u/256; u -= d2 * 256; 
       int d3 = u/16; u -= d3 * 16; 
       int d4 = u; 
       result += char('\\'); result += char('u'); 
       result += char(hexdigits[d1]); result += char(hexdigits[d2]); 
       result += char(hexdigits[d3]); result += char(hexdigits[d4]); 
      } 
      ++b; 
     } 
     return result; 
    } 
} } } 

Y la salida me sale:

{ 
    "text": "aáb" 
} 

También la función boost::property_tree::json_parser::a_unicode tienen problemas similares con la lectura escapó caracteres Unicode a caracteres firmados.

+1

Gracias por su respuesta. Buen hallazgo con 'boost :: property_tree :: json_parser :: create_escapes'. Tu solución es definitivamente una mejora. Pero no creo que funcione para todo el juego de caracteres UTF-8; /. ¿Estoy en lo cierto? –

+2

Todos los bytes que codifican caracteres Unicode sobre 0x7F son superiores a 0x7F (debajo de 0 para char firmado), por lo que esta función pasa correctamente a través de UTF-8. Algunos caracteres Unicode pueden no ser imprimibles, por supuesto, y alguna secuencia UTF-8 nunca debe aparecer. – Arpegius

+0

El estándar JSON no hace suposiciones sobre la codificación. Según RFC 46273. Codificación El texto JSON DEBE estar codificado en Unicode. La codificación predeterminada es UTF-8. Dado que los dos primeros caracteres de un texto JSON siempre serán caracteres ASCII [RFC0020], es posible determinar si un octeto es UTF-8, UTF-16 (BE o LE) o UTF-32 (BE o LE) mirando en el patrón de nulos en los primeros cuatro octetos. 00 00 00 xx UTF-32BE 00 xx 00 xx UTF-16BE xx 00 00 00 UTF-32LE xx 00 xx 00 UTF-16LE xx xx xx xx UTF-8 –

-1

soporte por encima Plano Multilingüe Básico:

template<class Ch> 
std::basic_string<Ch> create_escapes(const std::basic_string<Ch> &s) 
{ 
    std::basic_string<Ch> result; 
    typename std::basic_string<Ch>::const_iterator b = s.begin(); 
    typename std::basic_string<Ch>::const_iterator e = s.end(); 
    while (b != e) 
    { 
     if (*b == 0x20 || *b == 0x21 || (*b >= 0x23 && *b <= 0x2E) || 
      (*b >= 0x30 && *b <= 0x5B) || (*b >= 0x5D && *b <= 0x80)) 
      result += *b; 
     else if (*b == Ch('\b')) result += Ch('\\'), result += Ch('b'); 
     else if (*b == Ch('\f')) result += Ch('\\'), result += Ch('f'); 
     else if (*b == Ch('\n')) result += Ch('\\'), result += Ch('n'); 
     else if (*b == Ch('\r')) result += Ch('\\'), result += Ch('r'); 
     else if (*b == Ch('/')) result += Ch('\\'), result += Ch('/'); 
     else if (*b == Ch('"')) result += Ch('\\'), result += Ch('"'); 
     else if (*b == Ch('\\')) result += Ch('\\'), result += Ch('\\'); 
     else 
     { 
      const char * hexdigits = "ABCDEF"; 

      typedef typename make_unsigned<Ch>::type UCh; 
      unsigned long u = static_cast<unsigned long>(static_cast<UCh>(*b)); 

      if (u <= 0xFFFF) 
      {    
       int d1 = u/4096; u -= d1 * 4096; 
       int d2 = u/256; u -= d2 * 256; 
       int d3 = u/16; u -= d3 * 16; 
       int d4 = u; 

       result += Ch('\\'); result += Ch('u'); 
       result += Ch(hexdigits[d1]); result += Ch(hexdigits[d2]); 
       result += Ch(hexdigits[d3]); result += Ch(hexdigits[d4]); 
      } 
      else 
      { 
       u = (((static_cast<unsigned long>(static_cast<UCh>(*b)) - 0x10000) >> 10) & 0x3ff) + 0xd800; 

       int d1 = u/4096; u -= d1 * 4096; 
       int d2 = u/256; u -= d2 * 256; 
       int d3 = u/16; u -= d3 * 16; 
       int d4 = u; 

       result += Ch('\\'); result += Ch('u'); 
       result += Ch(hexdigits[d1]); result += Ch(hexdigits[d2]); 
       result += Ch(hexdigits[d3]); result += Ch(hexdigits[d4]); 

       u = ((static_cast<unsigned long>(static_cast<UCh>(*b)) - 0x10000) & 0x3ff) + 0xdc00; 

       d1 = u/4096; u -= d1 * 4096; 
       d2 = u/256; u -= d2 * 256; 
       d3 = u/16; u -= d3 * 16; 
       d4 = u; 

       result += Ch('\\'); result += Ch('u'); 
       result += Ch(hexdigits[d1]); result += Ch(hexdigits[d2]); 
       result += Ch(hexdigits[d3]); result += Ch(hexdigits[d4]); 
      } 
     } 
     ++b; 
    } 
    return result; 
} 
Cuestiones relacionadas