Пишу, по большей части, про историю, свою жизнь и немного про программирование.

Проблема в стемере для PHP

В модуле stem для ПХП обнаружилась неожиданная проблема. Этот модуль — стеммер, позволяющий обрезать окончания слов для того, чтобы искать потом по тексту без учёта падежей и склонений.

Для русского языка в нём целых две функции — для кодировки КОИ-8 и УТФ-8. Наш код работает в КС1251, потому для стеммера мы перегоняем текст в УТФ-8.

Я обратил внимание, что из командной строки код с этим модулем работает нормально, а в рабочем проекте кодировка портится — как будто стеммер где-то внутри себя рассматривает УТФ-8 как однобайтовую русскую кодировку и приводит её к нижнему регистру.

У меня возникла мысль, что дело в выставляемой локали, мы провели эксперимент — так и есть, дело в этом. Такой код работать правильно не будет:

setlocale(LC_TIME, 'ru_RU.cp1251');
echo stem_russian_unicode(iconv('windows-1251', 'utf-8', 'Приветы'));

Кодировка получится битой. Перед вызовом функций стеммера нужно переставлять кодировку (setlocale) в УТФ-8. Ошибочное место в модуле ищется элементарно:

// файл stem.c
     php_strtolower(z->p, arglen); // ← вот искомый вызов
     stem(z);
     z->p[z->l]= '\0';

// … ищем эту функцию в исходниках PHP…
// string.c из PHP
    PHPAPI char* php_strtolower(char * s, size_t len) {
        unsigned char *c, *e;

        c = (unsigned char *)s;
        e = c+len;

        while (c < e) {
            *c = tolower(*c); // ← а это проблемное место
            c++;
        }
         return s;
    }

Согласно манам, функция tolower ориентируется в своей работе на локаль, в этом и проблема.

7 комментариев
spiridonov@gmail.com 2013

Наверное, КС1251, а не КД1251?

Евгений Степанищев (bolknote.ru) 2013

Комментарий для spiridonov@gmail.com:

Да, спасибо. Ума не приложу почему написал «КД» :)

hshhhhh (hshhhhh.name) 2013

Комментарий для Евгения Степанищева:

Ну КС1251 это вообще апогей зла. Это вы так мастерски набросили? %)

Евгений Степанищев (bolknote.ru) 2013

Комментарий для hshhhhh.name:

Почему это апогей зла, я что-то не понял. 1251 — это кодовая страница. Что не так-то?

dinoelq 2013

ну , все привыкли «кодировка cp1251» , сразу всё понятно. А прочитав «КС1251» как-то не сразу понятно что имеется в виду.

Евгений Степанищев (bolknote.ru) 2013

Комментарий для dinoelq:

«Кодировка эсэр1251» :)

Люди вообще ко многому плохому привыкли. К «кавычкам» от печатающих машинок, отсутствию падежей у  Домодедово, к написанию McDonald’s на российских забегаловках этой сети, к юзанью иностранных слов, там где есть свои и так далее :)

Когда я писал «КС1251» я не имел ввиду «кодировка с названием „CP1251“», я имел ввиду «кодовая страница 1251».