【问题标题】:boost::property_tree::json_parser and two-byte wide charactersboost::property_tree::json_parser 和两字节宽的字符
【发布时间】:2012-05-02 21:25:17
【问题描述】:

简介

std::string text = "á";

“á”是两字节字符(假设为 UTF-8 编码)。
所以下面的行打印 2.

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

但是std::cout 仍然可以正确打印文本。

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

我的问题

我将text 传递给boost::property_tree::ptree,然后传递给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";

结果是

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

文本等于“á”,它不同于“á”。

是否可以在不切换到std::wstring 的情况下解决此问题?换库(boost::property_tree::ptree)有没有可能解决这个问题?

【问题讨论】:

    标签: c++ boost unicode boost-propertytree


    【解决方案1】:

    我找到了一些解决方案。 一般来说,您需要为[Ch=Char] 指定boost::property_tree::json_parser::create_escapes 模板,以提供您的“特殊场合无错误转义”。

    JSON 标准假定所有字符串都是用“\uXXXX”转义的 UTF-16 编码,但是一些库支持用“\xXX”转义的 UTF-8 编码。如果 JSON 文件可以用 UTF-8 编码,你可以传递所有高于 0x7F 的字符,女巫是为原始功能设计的。

    我在使用boost::property_tree::json_parser::write_json 之前输入了这段代码。来自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 = "0123456789ABCDEF";
                    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;
        }
    } } }
    

    我得到的输出:

    {
        "text": "aáb"
    }
    

    boost::property_tree::json_parser::a_unicode 函数在将转义的 unicode 字符读取为签名字符时也存在类似问题。

    【讨论】:

    • 感谢您的回答。很高兴找到boost::property_tree::json_parser::create_escapes。您的解决方案绝对是一种改进。但我认为它不适用于整个 UTF-8 字符集;/.我说的对吗?
    • 所有编码 Unicode 字符超过 0x7F 的字节都高于 0x7F(低于 0 表示有符号字符),所以这个函数正确地通过 UTF-8。当然,有些 unicode 字符可能无法打印,有些 UTF-8 序列绝对不能出现。
    • JSON 标准不对编码做任何假设。根据 RFC 46273。编码 JSON 文本应以 Unicode 编码。默认编码为 UTF-8。由于 JSON 文本的前两个字符始终是 ASCII 字符 [RFC0020],因此可以确定八位字节流是 UTF-8、UTF-16(BE 或 LE)还是 UTF-32(BE 或 LE)通过查看前四个八位字节中的空值模式。 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
    【解决方案2】:

    Boost 在 1.59 版本上修复了它。如果要升级版本,则需要小心。您可以从下面检查更改内容。 https://www.boost.org/users/history/version_1_59_0.html

    【讨论】:

      【解决方案3】:

      支持基本多语言平面以上:

          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 = "0123456789ABCDEF";
      
                  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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-09-14
        • 1970-01-01
        • 2018-01-04
        • 2018-01-11
        • 1970-01-01
        • 2015-08-14
        • 1970-01-01
        • 2023-03-21
        相关资源
        最近更新 更多