PHP: нечувствительность к регистру preg_replace кириллической строки в UTF8

У меня есть скрипт PHP 5.3, отображающий пользователей моего веб-сайта и хотел бы заменить некий российский город (хранящийся в UTF8 в базе данных PostgreSQL 8.4.7 + CentOS 5.5 / 64 бит Linux) по его более раннему имени (это инсайдерская шутка) :

preg_replace('/Волгоград/iu', 'Сталинград', $city); 

К сожалению, это работает только для точных матчей: Волгоград .

Это не работает для других случаев, таких как ВОЛГОГРАД или волгоград .

Если я изменю свой исходный код на

 preg_replace('/[Вв]олгоград/iu', 'Сталинград', $city); 

то он поймает второй случай выше.

Кто-нибудь знает, что происходит и как это исправить (если я не хочу писать [Xx] для каждой буквы)?

Спасибо! Alex

ОБНОВИТЬ:

 # rpm -qa|grep php php53-bcmath-5.3.3-1.el5 php53-gd-5.3.3-1.el5 php53-common-5.3.3-1.el5 php53-pdo-5.3.3-1.el5 php53-mbstring-5.3.3-1.el5 php53-xml-5.3.3-1.el5 php53-5.3.3-1.el5 php53-cli-5.3.3-1.el5 php53-pgsql-5.3.3-1.el5 # rpm -qa|grep pcre pcre-6.6-2.el5_1.7 

Я не могу воспроизвести вашу проблему с помощью PHP 5.3.3 ( PHP 5.3.3-1ubuntu9.3 with Suhosin-Patch (cli) ):

 $str1 = 'Волгоград'; $str2 = 'ВОЛГОГРАД'; $str3 = 'волгоград'; var_dump(preg_replace('/Волгоград/iu', 'Сталинград', $str1)); var_dump(preg_replace('/Волгоград/iu', 'Сталинград', $str2)); var_dump(preg_replace('/Волгоград/iu', 'Сталинград', $str3)); 

выходы

 string(20) "Сталинград" string(20) "Сталинград" string(20) "Сталинград" 

Какая версия PCRE использует ваш PHP? Проверьте phpinfo() для phpinfo() pcre . Это та, что в моей системе:

 ... pcre PCRE (Perl Compatible Regular Expressions) Support => enabled PCRE Library Version => 8.02 2010-03-19 ... 

Вы можете пропустить регулярное выражение, это сработало для меня в PHP 5.2.11 🙂

 $city = 'Unfortunately this only works for exact matches: Волгоград. This does not work for other cases, like ВОЛГОГРАД or волгоград.'; echo str_ireplace('Волгоград', '[found]', $city); 

Вывод

 "Unfortunately this only works for exact matches: [found]. This does not work for other cases, like [found] or [found]." 

Это заинтриговало меня, поэтому я задал вопрос .

Это решило проблему:

 setlocale(LC_ALL, 'ru_RU.CP1251', 'rus_RUS.CP1251', 'Russian_Russia.1251'); 

Я копирую + вставлял свой большой В Это действительно U+D092 , а не нормальный латинский B Но так как они похожи друг на друга: ВB Я считаю, что русское письмо сопоставляется с латиницей B U+0042 .

Таким образом, либо это предварительная форматирование PHP, либо, возможно, PCRE тоже неточно. Проверьте свою print PCRE_VERSION; и загляните в журнал изменений.

Во всяком случае, чтобы избежать проблемы, я предлагаю вам использовать только строчные буквы. Они, скорее всего, отличаются от латинского алфавита.

 preg_replace('/волгоград/iu', 'Сталинград', $city); 

PS: Зло внутри шутки!

Работает как очарование на моей коробке …

 <?php $city = 'Волгоград'; var_dump(preg_match('/волгоград/ui', $city)); var_dump(preg_match('/ВОЛГОГРАД/ui', $city)); var_dump(preg_replace('/волгоград/ui', 'Сталинград', $city)); var_dump(preg_replace('/ВОЛГОГРАД/ui', 'Сталинград', $city)); 

Вывод:

 int 1 int 1 string 'Сталинград' (length=20) string 'Сталинград' (length=20) 

Вы уверены, что входные данные ($ city) находятся в UTF8?

Просто угадать, но явно кодировать строку в Юникод может помочь:

 preg_replace('/Волгоград/iu', utf8_encode('Сталинград'), $city); 

Возможно, попробуйте: mb_eregi_replace http://www.php.net/manual/en/function.mb-eregi-replace.php

mb_eregi_replace – Заменить регулярное выражение на многобайтную поддержку, игнорируя регистр

На самом деле с PHP 5.2.x на окнах выбранный для решаемого ответа не работал для меня.

Мне пришлось переходить на Windows-1251, чтобы он работал.

Здесь вы приводите пример:

 $new_content = preg_replace(iconv('UTF-8', 'Windows-1251', "/\bгъз\b/i"), iconv('UTF-8', 'Windows-1251', "YYYYYY"), iconv('UTF-8', 'Windows-1251', "ти си gyz gyz гъз ГЪЗ gyzgyz гЪз gyz")); $new_content = iconv('Windows-1251', 'UTF-8', $new_content); 

Приведенный выше пример заменит успешно (без учета случая) «гъз» на YYYYYY и вернет вам версию UTF-8.

С уважением!

для тех, кто поддерживает огромную базу устаревших кодов, борясь с проблемами кодировки и кодирования и без возможности конвертировать кодировку кода – вот ответ:

 //for setlocale(LC_ALL, 'ru_RU.cp1251'); //(or any other locale) to take effect, //you MUST generate system locale, ie sudo su #view supported locales #less /usr/share/i18n/SUPPORTED echo "ru_RU.cp1251 CP1251" >> /var/lib/locales/supported.d/local dpkg-reconfigure locales exit #and (for ubuntu/debian) apt-get install php5-intl 

в то время как вы можете переписать свое регулярное выражение для использования некоторых трюков utf, конвертировать ваш код в utf, это не вариант, когда вы работаете с огромной базой данных / базы данных и т. д.