Работа с Unicode в Cython

381
16 июня 2017, 10:09

Имеется скрипт, написанный на Cython, который принимает на вход массив C++ строк, анализирует их с помощью pymorphy2 и обрабатывает результаты анализа. В процессе сборки данный скрипт транслируется в исходный код на C++ с помощью следующей команды:

cython ${SCRIPT_FILENAME} -o "morph-analyzer.cpp" -2

В данном скрипте определена функция, в которой осуществляется взаимодействие с pymorphy2:

cdef analyzeToken(const UnicodeString tok, vector[pair[UnicodeString, vector[shared_ptr[AnalysisResult]]]] &analysis_results):
    cdef vector[shared_ptr[AnalysisResult]] res_for_tok    
    cdef shared_ptr[AnalysisResult] analysis_res
    cdef string utf8_str
    tok.toUTF8String(utf8_str)
    morph_results = morph.parse((utf8_str.c_str()).decode('UTF-8'))
    cdef unsigned propMask
    for morph_result in morph_results:
        analysis_res = make_shared[AnalysisResult]()
        if morph_result.score > 0.1:
            deref(analysis_res).normalForm = UnicodeString(morph_result.normal_form.encode('UTF-8'))
            propMask = 0 
            if morph_result.tag.POS is not None:
                deref(analysis_res).partOfSpeech = UnicodeString(morph_result.tag.POS.encode('UTF-8'))
            if 'Name' in morph_result.tag:
                propMask |= FIRST_NAME
            if 'Patr' in morph_result.tag:
                propMask |= PATR
            if 'Surn' in morph_result.tag:
                propMask |= SECOND_NAME
            if 'Init' in morph_result.tag:
                propMask |= INIT
            if 'Geox' in morph_result.tag:
                propMask |= GEOX
            deref(analysis_res).nameCharMask = propMask
        else:
            deref(analysis_res).normalForm = tok
        res_for_tok.push_back(analysis_res)
     analysis_results.push_back(pair[UnicodeString, vector[shared_ptr[AnalysisResult]]](tok, res_for_tok))

Входная строка, которую нужно проанализировать, подается на вход данной функции как объект класса UnicodeString (из библиотеки icu4c) под именем tok. Первым делом производится преобразование переданной строки в объект класса std::string в кодировке utf-8:

cdef string utf8_str
tok.toUTF8String(utf8_str)

Далее, нужно передать эту строку на вход функции pymorphy2.MorphAnalyzer.parse(), которая принимает родные строки из Python в формате Unicode. Чтобы преобразовать имеющуюся utf-8 строку в нужный формат, вызывается метод bytes.decode('UTF-8').

При обработке результатов от pymoprhy2 производится обратное преобразование полученной строки в формат utf-8 с помощью функции encode('UTF-8'), после чего результат передается на вход конструктору UnicodeString.

Приведенный выше код корректно работает в системе Linux при использовании Python 2.7.13 and Cython 0.25.2. Однако в Windows при использовании тех же версий Python и Cython строки каким-то образом искажаются, из-за чего pymorphy2 оказывается не в состоянии их обработать. Ниже приведен результат искажения:

Current token: Original word: нижес▒едующем, normal form: ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒␰▒

Должно быть так:

Current token: Original word: нижеследующем, normal form: нижеследующий

Подскажите, пожалуйста, в чем может быть проблема. Хотел бы еще раз подчеркнуть тот факт, что проявляется она только при работе в Windows (версия 8.1).

Update:

Функция analyzeToken вызывается из другой функции, определенной в том же скрипте:

cdef public analyzeTokens(const vector[UnicodeString] &tokens, map[UnicodeString, vector[shared_ptr[AnalysisResult]]] &analysis_results):
for i in xrange(tokens.size()):
    analyzeToken(tokens[i], analysis_results)

которая, в свою очередь, вызывается уже из C++ кода

    std::map<UnicodeString, std::vector<std::shared_ptr<AnalysisResult>>> analysisResults;
    analyzeTokens(plainTokens, analysisResults);

где plainTokens - std::vector<UnicodeString>. Ключевое слово public в сигнатуре функции заставляет Cython создавать заголовочный файл, содержащий ее объявление.

Update2:

Изначально текстовые данные считываются из файла:

static bool readAllTextFromFile(const std::string &filename, UnicodeString &outText) {
    std::ifstream ifs(filename);
    if (ifs.fail()) {
        std::cerr << "Error: failed to open " << filename << std::endl;
        return false;
    }
    std::string text {std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>()};
    outText = UnicodeString(text.c_str());
    return true;
}

Далее считанные текстовые данные проходят некоторую структурную обработку (разбиваются на массивы строк с помощью регулярных выражений) и подаются на вход функции analyzeTokens:

std::vector<UnicodeString> plainSentences;
split_unistring(plainText, {"\\?","\\.","\\!"}, plainSentences);
for (auto &plainSentence : plainSentences) {
    Logger::getLogger() << plainSentence << std::endl;
    std::vector<UnicodeString> plainTokens;
    split_unistring(plainSentence, {"\\s","\\;","\\:","\\,"}, plainTokens);
    Sentence currentSentence;
    std::map<UnicodeString, std::vector<std::shared_ptr<AnalysisResult>>> analysisResults;
    analyzeTokens(plainTokens, analysisResults);
    ...
}
READ ALSO
Exteption Throw -&gt; ..WSASocketW(.. - socket_ops.ipp - library boost asio [требует правки]

Exteption Throw -> ..WSASocketW(.. - socket_ops.ipp - library boost asio [требует правки]

Иногда при коннекте вылетает исключение

261
Ошибка при получении данных из item?

Ошибка при получении данных из item?

Добрый день, есть таблица в tabeWidget из её строки пытаюсь получить копию во вторую tabeWidget_2 данной конструкцией

264
C++ Set Wallpaper

C++ Set Wallpaper

Всем привет, надо мне сменить фон рабочего стола, юзаю функцию SystemParametersInfo() "Windowsh" подключил, путь прописал, по нажатию кнопки должна поставиться...

331
Программа по поиску слова в другом другом слове. c++ [требует правки]

Программа по поиску слова в другом другом слове. c++ [требует правки]

Дали на сессии задание, написать программу в которую вводишь 2 слова, и после этого он выделяет первое слово во втором, пример (сам-САМолёт)...

221