PHP: RegExp проблем с кирилицата

Публикувано на 27 април 2006 от Ицката в Интернет и уеб

За един от последните си сайтове писах една търсачка, на php. За търсенето ползвах регулярни изрази (т. нар. regular expressions), по-конкретно функцията preg_match. Като първи параметър на функцията се задава филтъра, по който се прави търсенето. Ако в края на стринга за филтъра има "i", това означава, че се прави case insensitive match, т.е. търсене без да прави разлика между главни и малки букви.

Дотук добре – реших да ползвам тази опция, дали търсенето да прави разлика между големи и малки букви или не. В първия случай слагам във филтъра думата, по която се търси, а във втория – отзад слагам едно i – за case insensitive. Вкъщи сървъра ми е Windows базиран – нямаше проблеми – функцията работеше. Сървъра дето се хоства сайта, обаче, е Линукс и имаше проблем с кирилицата – дори да му задавам case insensitive match, то винаги си правеше case sensitive, и то само за кирилицата – голям проблем.

Поразпитах из БГ форумите, най-адекватно решение ми даде Бабата от ФлашБГ (за което съм много благодарен) – във филтъра всяка буква от думата по която се търси да я правя в група с голяма и малка буква. Не ме разбрахте? Пример: търсиме за думата ‘нещо’, искаме да не прави разлика между големи и малки букви. Стандартно филтъра щеше да ни е от вида

preg_match ("/нещо/i",$text);

обаче туй нящо под Линукс не ще. Ще ни върне само и само думата "нещо", като думи като "Нещо", "нЕщО" и "неЩО" например ще пропусне. Решението на Бабата:

preg_match ("/[Нн][Ее][Щщ][Оо]/",$text);

За какво иде реч? [ и ] означават, че се търси само за един от знаците между тях, т.е. в нашия случай [Нн] ще търси за Н или н, което ни устройва. Дотук добре, остава обаче да напишем функция за създаване на подобен филтър динамично, т.е. имаме променлива с ключовата дума, задаваме я на функцията и тя ни връща филтъра. Ето какво написах:

// Конкретно на моя сървър фунцкиите strtolower и strtoupper отказаха да работят за кирилица, затова си написах мои:

function cstrtolower($str) {
   return strtr($str, "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯABCDEFGHIJKLMNOPQRSTUVWXYZ", "абвгдежзийклмнопрстуфхцчшщъьюяabcdefghijklmnopqrstuvwxyz");
}

function cstrtoupper($str) {
   return strtr($str, "абвгдежзийклмнопрстуфхцчшщъьюяabcdefghijklmnopqrstuvwxyz", "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯABCDEFGHIJKLMNOPQRSTUVWXYZ");
}

// Същинската функция:

function createFilter ($string) {
    $string = cstrtolower($string);
    for ($i=0;$i<strlen($string);$i++) {
        $letter_small = substr($string,$i,1);
        $letter_big = cstrtoupper($letter_small);
        $newstr .= ‘['.$letter_small.$letter_big.']‘;
    }
    return $newstr;
}

Това е, надявам се на някого да свърши работа. А – и друго – при мен също не работеше филтъра ако искам да търси за цяла дума, това пак при кирилицата. Решението: вместо

preg_match ("/\b[Нн][Ее][Щщ][Оо]\b/", $text);

ползвайте

preg_match ("/[^A-Za-zА-Яа-я][Нн][Ее][Щщ][Оо][^A-Za-zА-Яа-я]/", $text);

Полезен сайт за регулярните изрази и филтрите.

  1. Проблем с клавиатура Да, оказа се, че и с новата клавиатура имам проблем – същият като при старата, и не точно. За какво...