2013-10-09 1 views
3

У меня есть аналогичная проблема, например How to use boost::spirit to parse UTF-8? и How to match unicode characters with boost::spirit?, но ни одна из этих проблем не решена. У меня есть std::string с UTF-8 символов, я использовал u8_to_u32_iterator обернуть std::string и использовать unicode терминалы, как это:Обработка utf-8 в Boost.Spirit с парсером utf-32

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); 
     } 
    } 

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) 
    ) 

Это прекрасно работает, но мне было интересно, может я как-то удается сделать синтаксический анализатор непосредственно присваивает ей на структуру, содержащую std::string членов вместо того, чтобы делать для каждого цикла, с u32_to_u8_iterator? Я думал, что один способ может сделать оболочку для std :: string, которая будет иметь оператор присваивания с boost::uint32_t, чтобы парсер мог назначить напрямую, но есть ли другие решения?

EDIT

После прочтения некоторых еще я закончил с этим:

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 

и это

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 
      ); 
    } 

Любые комментарии или советы, если это лучшее, что я могу получить?

ответ

4

Другая работа для атрибута атрибута . Я упростил свои типы данных для демонстрационных целей:

typedef std::basic_string<uint32_t> u32_string; 

struct Value 
{ 
    std::string value; 
}; 

Теперь вы можете иметь преобразование произойдет «авто-магически» с помощью:

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())); 
      } 
     }; 
}}} 

Рассмотрима примера синтаксического анализа, который анализирует строку JSON-стиль в UTF -8, а также позволяет Unicode escape-последовательностям 32-битных кодовых точек: \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_; 
}; 

Как вы можете видеть, 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; 
} 

Теперь заметим, что выход кодируется в 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   

U+1E61 code-point был правильно закодирован как [0xE1,0xB9,0xA1].

+0

Спасибо, проверим это и примем, если он решает мою проблему :) –

+0

Извините за долгую задержку, но мне нужно дополнительное руководство - мой парсер (как в примере) всегда вызывает 'assign_to_attribute_from_value' с параметрами шаблона' char' и 'unsigned int' (что имеет смысл с тех пор), заключается в том, что из-за грамматики? Есть ли способ заставить его использовать int32_t внутри, а затем назначить целую 'u32_string' на' std :: string'? Я определенно чего-то не хватает. –

+0

Да, это возможно, это то, что я делаю в своем ответе. Для сравнения, посмотрите на «no_wide» ветви моего игрушечного парсера JSON ([в частности «https://github.com/sehe/spirit-v2-json/blob/nowide/JSON.cpp#L48'](https:/ /github.com/sehe/spirit-v2-json/blob/nowide/JSON.cpp#L48)), если вы (хотите) иметь его другим способом. – sehe

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

  • Нет связанных вопросов^_^