使用utf-32解析器处理Boost.Spirit中的utf-8

Rud*_*lis 4 c++ boost utf-8 boost-spirit

我有一个类似的问题,比如如何使用boost :: spirit来解析UTF-8?以及如何使用boost :: spirit匹配unicode字符?但这些都没有解决我面临的问题.我有一个std::stringUTF-8字符,我用它u8_to_u32_iterator来包装std::string和使用这样的unicode终端:

BOOST_NETWORK_INLINE void parse_headers(std::string const & input, std::vector<request_header_narrow> & container) {
        using namespace boost::spirit::qi;
        u8_to_u32_iterator<std::string::const_iterator> begin(input.begin()), end(input.end());
        std::vector<request_header_narrow_utf8_wrapper> wrapper_container;
        parse(
            begin, end,
            *(
                +(alnum|(punct-':'))
                >> lit(": ")
                >> +((unicode::alnum|space|punct) - '\r' - '\n')
                >> lit("\r\n")
            )
            >> lit("\r\n")
            , wrapper_container
            );
        BOOST_FOREACH(request_header_narrow_utf8_wrapper header_wrapper, wrapper_container)
        {
            request_header_narrow header;
            u32_to_u8_iterator<request_header_narrow_utf8_wrapper::string_type::iterator> name_begin(header_wrapper.name.begin()),
                                                                                          name_end(header_wrapper.name.end()),
                                                                                          value_begin(header_wrapper.value.begin()),
                                                                                          value_end(header_wrapper.value.end());
            for(; name_begin != name_end; ++name_begin)
                header.name += *name_begin;
            for(; value_begin != value_end; ++value_begin)
                header.value += *value_begin;
            container.push_back(header);
       }
    }
Run Code Online (Sandbox Code Playgroud)

这样request_header_narrow_utf8_wrapper定义并映射到Fusion(不介意丢失的名称空间声明):

struct request_header_narrow_utf8_wrapper
{
    typedef std::basic_string<boost::uint32_t> string_type;
    std::basic_string<boost::uint32_t> name, value;
};

BOOST_FUSION_ADAPT_STRUCT(
    boost::network::http::request_header_narrow_utf8_wrapper,
    (std::basic_string<boost::uint32_t>, name)
    (std::basic_string<boost::uint32_t>, value)
    )
Run Code Online (Sandbox Code Playgroud)

这工作正常,但我想知道我可以以某种方式设法使解析器直接分配给包含std::string成员的结构,而不是使用u32_to_u8_iterator?执行for-each循环?我认为有一种方法可以为std :: string创建一个包装器,它将具有赋值运算符,boost::uint32_t以便解析器可以直接分配,但是还有其他解决方案吗?

编辑

在阅读了一些之后我最终得到了这个:

namespace boost { namespace spirit { namespace traits {

    typedef std::basic_string<uint32_t> u32_string;

   /* template <>
    struct is_string<u32_string> : mpl::true_ {};*/

    template <> // <typename Attrib, typename T, typename Enable>
    struct assign_to_container_from_value<std::string, u32_string, void>
    {
        static void call(u32_string const& val, std::string& attr) {
            u32_to_u8_iterator<u32_string::const_iterator> begin(val.begin()), end(val.end());
            for(; begin != end; ++begin)
                attr += *begin;
        }
    };

} // namespace traits

} // namespace spirit

} // namespace boost
Run Code Online (Sandbox Code Playgroud)

还有这个

BOOST_NETWORK_INLINE void parse_headers(std::string const & input, std::vector<request_header_narrow> & container) {
        using namespace boost::spirit::qi;
        u8_to_u32_iterator<std::string::const_iterator> begin(input.begin()), end(input.end());
        parse(
            begin, end,
            *(
                as<boost::spirit::traits::u32_string>()[+(alnum|(punct-':'))]
                >> lit(": ")
                >> as<boost::spirit::traits::u32_string>()[+((unicode::alnum|space|punct) - '\r' - '\n')]
                >> lit("\r\n")
            )
            >> lit("\r\n")
            , container
            );
    }
Run Code Online (Sandbox Code Playgroud)

如果这是我能得到的最好的评论或建议吗?

seh*_*ehe 5

属性特征的另一个工作.为了演示目的,我简化了数据类型:

typedef std::basic_string<uint32_t> u32_string;

struct Value 
{
    std::string value;
};
Run Code Online (Sandbox Code Playgroud)

现在,您可以使用以下方式"自动神奇地"进行转换:

namespace boost { namespace spirit { namespace traits {
    template <> // <typename Attrib, typename T, typename Enable>
        struct assign_to_attribute_from_value<Value, u32_string, void>
        {
            typedef u32_to_u8_iterator<u32_string::const_iterator> Conv;

            static void call(u32_string const& val, Value& attr) {
                attr.value.assign(Conv(val.begin()), Conv(val.end()));
            }
        };
}}}
Run Code Online (Sandbox Code Playgroud)

考虑一个解析UTF-8中JSON样式字符串的示例解析器,同时还允许32位代码点的Unicode转义序列:\uXXXX.将中间存储器u32_string用于此目的很方便:

///////////////////////////////////////////////////////////////
// Parser
///////////////////////////////////////////////////////////////

namespace qi         = boost::spirit::qi;
namespace encoding   = qi::standard_wide;
//namespace encoding = qi::unicode;

template <typename It, typename Skipper = encoding::space_type>
    struct parser : qi::grammar<It, Value(), Skipper>
{
    parser() : parser::base_type(start)
    {
        string = qi::lexeme [ L'"' >> *char_ >> L'"' ];

        static qi::uint_parser<uint32_t, 16, 4, 4> _4HEXDIG;

        char_ = +(
                ~encoding::char_(L"\"\\")) [ qi::_val += qi::_1 ] |
                    qi::lit(L"\x5C") >> (                    // \ (reverse solidus)
                    qi::lit(L"\x22") [ qi::_val += L'"'  ] | // "    quotation mark  U+0022
                    qi::lit(L"\x5C") [ qi::_val += L'\\' ] | // \    reverse solidus U+005C
                    qi::lit(L"\x2F") [ qi::_val += L'/'  ] | // /    solidus         U+002F
                    qi::lit(L"\x62") [ qi::_val += L'\b' ] | // b    backspace       U+0008
                    qi::lit(L"\x66") [ qi::_val += L'\f' ] | // f    form feed       U+000C
                    qi::lit(L"\x6E") [ qi::_val += L'\n' ] | // n    line feed       U+000A
                    qi::lit(L"\x72") [ qi::_val += L'\r' ] | // r    carriage return U+000D
                    qi::lit(L"\x74") [ qi::_val += L'\t' ] | // t    tab             U+0009
                    qi::lit(L"\x75")                         // uXXXX                U+XXXX
                        >> _4HEXDIG [ qi::_val += qi::_1 ]
                );

        // entry point
        start = string;
    }

    private:
    qi::rule<It, Value(),  Skipper> start;
    qi::rule<It, u32_string()> string;
    qi::rule<It, u32_string()> char_;
};
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,start规则只是将属性值赋给Value结构 - 这隐含地调用了我们的assign_to_attribute_from_value特征!

一个简单的测试程序Live on Coliru来证明它确实有效:

// input assumed to be utf8
Value parse(std::string const& input) {
    auto first(begin(input)), last(end(input));

    typedef boost::u8_to_u32_iterator<decltype(first)> Conv2Utf32;
    Conv2Utf32 f(first), saved = f, l(last);

    static const parser<Conv2Utf32, encoding::space_type> p;

    Value parsed;
    if (!qi::phrase_parse(f, l, p, encoding::space, parsed))
    {
        std::cerr << "whoops at position #" << std::distance(saved, f) << "\n";
    }

    return parsed;
}

#include <iostream>

int main()
{
    Value parsed = parse("\"Footnote: ¹ serious busine?\\u1e61\n\"");
    std::cout << parsed.value;
}
Run Code Online (Sandbox Code Playgroud)

现在观察输出再次以UTF8编码:

$ ./test | tee >(file -) >(xxd)

Footnote: ¹ serious busine??
/dev/stdin: UTF-8 Unicode text
0000000: 466f 6f74 6e6f 7465 3a20 c2b9 2073 6572  Footnote: .. ser
0000010: 696f 7573 2062 7573 696e 65c5 9fe1 b9a1  ious busine.....
0000020: 0a        
Run Code Online (Sandbox Code Playgroud)

U + 1E61码点已被正确地编码为[0xE1,0xB9,0xA1].