2012-04-21 8 views
8

Wprowadzenie

std::string text = "á"; 

"A" ma postać dwóch bajtów (zakładając kodowanie UTF-8).
Więc następujące odciski linii 2.boost :: property_tree :: json_parser i dwu-bajtowych szerokości znaków

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

Ale std::cout nadal drukuje tekst poprawnie.

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

Mój problem

mijam text do boost::property_tree::ptree a następnie 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"; 

Rezultatem jest

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

tekst jest równy "Â ¡", która jest inna niż "á".

Czy można naprawić ten problem bez przełączania na std::wstring? Czy jest możliwe, że zmiana biblioteki (boost::property_tree::ptree) może rozwiązać ten problem?

Odpowiedz

10

Znalazłem kilka rozwiązań. Ogólnie należy podać szablon boost::property_tree::json_parser::create_escapes dla [Ch=Char], aby zapewnić "wyjątkową okazję bez ucieczki".

Standard JSON zakłada, że ​​wszystkie ciągi są kodowane UTF-16 z "\ uXXXX", ale niektóre biblioteki obsługują kodowanie UTF-8 z "\ xXX". Jeśli plik JSON może być zakodowany w UTF-8, możesz przekazać wszystkie znaki o wartości wyższej niż 0x7F, które były przeznaczone do pierwotnej funkcji.

Umieściłem ten kod przed użyciem boost::property_tree::json_parser::write_json. Pochodzi z 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; 
    } 
} } } 

a wyjście uzyskać:

{ 
    "text": "aáb" 
} 

Również funkcja boost::property_tree::json_parser::a_unicode mają podobne problemy z czytaniem uciekł znaków Unicode podpisanych znaków.

+1

Dzięki za odpowiedź. Ładne znalezisko z 'boost :: property_tree :: json_parser :: create_escapes'. Twoje rozwiązanie zdecydowanie stanowi ulepszenie. Ale nie sądzę, że działa to dla całego zestawu znaków UTF-8; /. Czy mam rację ? –

+2

Wszystkie bajty kodu Unicode powyżej 0x7F znajdują się powyżej 0x7F (poniżej 0 dla podpisanego znaku), więc ta funkcja poprawnie przechodzi przez UTF-8. Niektóre znaki unicode mogą nie być oczywiście drukowane, a niektóre sekwencje UTF-8 nigdy nie mogą się pojawić. – Arpegius

+0

Standard JSON nie zawiera żadnych założeń dotyczących kodowania. Zgodnie z RFC 46273. Kodowanie Tekst JSON MUSI być zakodowany w Unicode. Domyślne kodowanie to UTF-8. Ponieważ pierwsze dwa znaki tekstu JSON zawsze będą ASCII znaków [RFC0020], możliwe jest określenie, czy oktetowy strumień to kodowanie UTF-8, UTF-16 (BE lub LE) lub UTF-32 (BE lub LE), wyszukując według wzorca zer w pierwszych czterech oktetach. 00 00 00 XX UTF-32BE 00 X X 00 X X UTF-16BE XX 00 00 00 UTF-32LE X X 00 X X 00 UTF-16LE XX XX XX XX UTF-8 –

-1

Pomoc powyżej podstawowe Multilingual Plane:

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; 
} 
Powiązane problemy