Поиск различных вариантов транслитерации русских текстов (ФИО) латиницей для выборок в MySQL

от автора

Собственно, столкнулся с банальной задачей — есть база, в которой есть масса данных ФИО, написанных латиницей. Зачастую достаточно безграмотно и непредсказуемо транслитерировано. Вроде бы и есть ГОСТ 7.79 (ISO 9), но на практике его далеко не всегда соблюдают.
Задача — есть БД с записями (как правило, ФИО) латиницей. Плохой латиницей. Требуется по русской подстроке поиска в вэб-приложении выбрать из БД (MySQL) записи, похоже транслитерированные на латиницу.


Первая мысль — написать функцию, которая возвращала бы массив, содержащий возможные написания исходной фразы латиницей.

Исходник 1, позволяющий взглянуть как может быть транслитерирована банальная строка

print_r (r2e ("Очень простая фраза на русском языке"));  function r2e ($txt)  {   $r=array(mb_strtolower($txt,"windows-1251"));   $r=r2es($r,"/кс/i",array("x","ks","cs"));   $r=r2es($r,"/ей/i",array("ey","ei"));   $r=r2es($r,"/ов/i",array("ov","off"));   $r=r2es($r,"/а/i",array("a"));   $r=r2es($r,"/б/i",array("b"));   $r=r2es($r,"/в/i",array("v","w"));   $r=r2es($r,"/г/i",array("g"));   $r=r2es($r,"/д/i",array("d"));   $r=r2es($r,"/е/i",array("e"));   $r=r2es($r,"/ё/i",array("yo","jo"));   $r=r2es($r,"/ж/i",array("zh","z"));   $r=r2es($r,"/з/i",array("z","s"));   $r=r2es($r,"/и/i",array("i"));   $r=r2es($r,"/й/i",array("j","y"));   $r=r2es($r,"/к/i",array("k","c"));   $r=r2es($r,"/л/i",array("l"));   $r=r2es($r,"/м/i",array("m"));   $r=r2es($r,"/н/i",array("n"));   $r=r2es($r,"/о/i",array("o"));   $r=r2es($r,"/п/i",array("p"));   $r=r2es($r,"/р/i",array("r"));   $r=r2es($r,"/с/i",array("s"));   $r=r2es($r,"/т/i",array("t"));   $r=r2es($r,"/у/i",array("u"));   $r=r2es($r,"/ф/i",array("f"));   $r=r2es($r,"/х/i",array("h","kh"));   $r=r2es($r,"/ц/i",array("c","ts"));   $r=r2es($r,"/ч/i",array("ch"));   $r=r2es($r,"/ш/i",array("sh"));   $r=r2es($r,"/щ/i",array("shch","sch","sh"));   $r=r2es($r,"/ъ/i",array(""));   $r=r2es($r,"/ы/i",array("y"));   $r=r2es($r,"/ь/i",array(""));   $r=r2es($r,"/э/i",array("e"));   $r=r2es($r,"/ю/i",array("u","yu","ju"));   $r=r2es($r,"/я/i",array("ya","ja"));   return $r;  }  function r2es ($var, $pattern, $splits)  {   $sp=array(); $nsp=array();   foreach ($var as $v)     if (preg_match($pattern,$v)) foreach ($splits as $split) $sp=array_merge($sp,array(preg_replace($pattern,$split,$v)));     else $nsp=array_merge($nsp,array($v));   return array_merge($sp,$nsp);  } 

Содержимое выходного массива из примера 1

Array
(
[0] => ochen prostaya fraza na russkom yazyke
[1] => ochen prostaja fraza na russkom jazyke
[2] => ochen prostaya fraza na russcom yazyce
[3] => ochen prostaja fraza na russcom jazyce
[4] => ochen prostaya frasa na russkom yasyke
[5] => ochen prostaja frasa na russkom jasyke
[6] => ochen prostaya frasa na russcom yasyce
[7] => ochen prostaja frasa na russcom jasyce
)

вроде и хорошо получилось, даже познавательно, но полученный массив нужно ещё и склеить в довесок к запросу в виде

$sqlstr = "SELECT <smth> FROM <smwhr> WHERE searchField LIKE '%" . implode("%' OR searchField LIKE '%",$result) . "%'"; либо, если нужно точное совпадение $sqlstr = "SELECT <smth> FROM <smwhr> WHERE searchField LIKE '" . implode("' OR searchField LIKE '",$result) . "'"; 

Ну и технически настораживает, что запрос SQL совершенно легко может удлинниться в разы.
Зато с минимальными доработками будет работать практически с любым движком БД.

Решение задачи оказалось значительно компактнее — MySQL поддерживает вместо «SELECT… WHERE field LIKE ‘pattern’» продвинутые конструкции «SELECT… WHERE field REGEXP ‘regexp’», позволяющие выполнять поиск по regexp’ам.

Итого — компактный код со значительно лучшими показателями при работе с MySQL

Так значительно лучше

function r2e ($txt)  {   $txt=mb_strtolower($txt,"windows-1251");   $sr=array("кс","ей","ов","а","б","в","г","д","е","ё","ж","з","и","й","к","л","м","н","о","п","р","с","т","у","ф","х","ц","ч","ш","щ","ъ","ы","ь","э","ю","я");   $se=array("(x|[kc]s)","e[yi]","o(v|ff)","a","b","[vw]","g","d","e","[yj]o","z(h)*","[zs]","i","[jy]","[kc]","l","m","n","o","p","r","s","t","u","f","(k|c)*h","(c|ts)","ch","sh","s(h)*(c)*h","","y","","e","[yj]*u","[yj]a");   return str_replace($sr,$se,$txt);  } 

в результате возвращается строка вида

ochen prosta[yj]a fra[zs]a na russ[kc]om [yj]a[zs]y[kc]e 

вполне пригодная для вставки в «SELECT columns FROM table WHERE searchField REGEXP ‘$txt’» в качестве, собственно, $txt.

А что с другими популярными СУБД?
— К сожалению, с MSSQL все оказалось не столь радужно — хотя можно использовать CLR-модуль (например, отсюда) и получить поддержку регулярных выражений.
— С T-SQL (Oracle) все хорошо, по крайней мере с 10G версии есть REGEXP_LIKE(fieldName, regexpString).
— в PostgreSQL есть достаточно давно regexp_matches(fieldName, regexpString).
— в FireBird можно использовать SIMILAR TO, при этом придется переработать синтаксис, например для славно русской «Щ» вместо s(h)*(с)*h нужно будет описать как s(h){0,1}©{0,1}h и далее по аналогии.

ссылка на оригинал статьи http://habrahabr.ru/post/202528/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *