Perl регулярное выражение задыхается на нескольких экземплярах наборов символов - PullRequest
4 голосов
/ 22 ноября 2010

Я начал с некоторых сумасшедших сбоев, используя preg_replace в php, и свел его к проблемному случаю, когда несколько классов символов использовали турецкую «i» и unotted «i» вместе. Вот простой тестовый пример в php:

<?php
    echo 'match single normal i: ';
    $str = 'mi';
    echo (preg_match('!m[ıi]!', $str)) ? "ok\n" : "fail\n";

    echo 'match single undotted ı: ';
    $str = 'mı';
    echo (preg_match('!m[ıi]!', $str)) ? "ok\n" : "fail\n";

    echo 'match double normal i: ';
    $str = 'misir';
    echo (preg_match('!m[ıi]s[ıi]r!', $str)) ? "ok\n" : "fail\n";

    echo 'match double undotted ı: ';
    $str = 'mısır';
    echo (preg_match('!m[ıi]s[ıi]r!', $str)) ? "ok\n" : "fail\n";
?>

И снова тот же тест в Perl:

#!/usr/bin/perl

$str = 'mi';
$str =~ m/m[ıi]/ && print "match single normal i\n";

$str = 'mı';
$str =~ m/m[ıi]/ && print "match single undotted ı\n";

$str = 'misir';
$str =~ m/m[ıi]s[ıi]r/ && print "match double normal i\n";

$str = 'mısır';
$str =~ m/m[ıi]s[ıi]r/ && print "match double undotted ı\n";

Первые три теста работают нормально. Последний не совпадает.

Почему это нормально работает как класс символов один раз, а не второй раз в том же выражении? Как мне написать выражение, которое должно соответствовать подобному слову, независимо от того, с какими сочетаниями букв оно написано?

Редактировать: Фон на языковая проблема Я пытаюсь запрограммировать на

Редактировать 2: Добавление директивы use utf8; исправляет версию perl. Так как моя первоначальная проблема была с программой php, и я переключился на perl только для того, чтобы посмотреть, была ли это ошибка в php, это не очень мне помогает. Кто-нибудь знает директиву, чтобы PHP не подавился этим?

Ответы [ 2 ]

8 голосов
/ 22 ноября 2010

Возможно, вам придется сообщить Perl, что ваш исходный файл содержит символы utf8.Попробуйте:

#!/usr/bin/perl

use utf8;   # **** Add this line

$str = 'mısır';
$str =~ m/m[ıi]s[ıi]r/ && print "match double undotted ı\n";

Что не помогает вам с PHP, но в PHP может быть похожая директива.В противном случае попробуйте использовать некоторую форму escape-последовательности, чтобы избежать вставки буквенного символа в ваш исходный код.Я ничего не знаю о PHP, поэтому ничего не могу с этим поделать.

Редактировать
Я читаю, что PHP не поддерживает Unicode.Таким образом, ввод Unicode, который вы передаете, скорее всего рассматривается как строка байтов, в которой Unicode был закодирован как.

Если вы можете быть уверены, что ваш ввод поступает как utf-8, тогда вы можете сопоставитьUTF-8 последовательность для ı, которая \xc4 \xb1, как в:

$str = 'mısır';  # Make sure this source-file is encoded as utf-8 or this match will fail
echo (preg_match('!m(i|\xc4\xb1)s(i|\xc4\xb1)r!', $str)) ? "ok\n" : "fail\n";

Это работает?

Изменить еще раз:
Я могу объяснитьпочему ваши первые три теста проходят.Давайте представим, что в вашей кодировке ı кодируется как ABCDE.тогда PHP видит следующее:

echo 'match single normal i: ';
$str = 'mi';
echo (preg_match('!m[ABCDEi]!', $str)) ? "ok\n" : "fail\n";

echo 'match single undotted ABCDE: ';
$str = 'mABCDE';
echo (preg_match('!m[ABCDEi]!', $str)) ? "ok\n" : "fail\n";

echo 'match double normal i: ';
$str = 'misir';
echo (preg_match('!m[ABCDEi]s[ABCDEi]r!', $str)) ? "ok\n" : "fail\n";

echo 'match double undotted ABCDE: ';
$str = 'mABCDEsABCDEr';
echo (preg_match('!m[ABCDEi]s[ABCDEi]r!', $str)) ? "ok\n" : "fail\n";

, что делает очевидным, почему первые три теста проходят, а последний не проходит.Если вы используете начальный / конечный якорь ^...$, я думаю, вы обнаружите, что только первый тест пройден.

4 голосов
/ 23 ноября 2010

Многобайтовые последовательности не будут делать то, что вы хотите в классах char в квадратных скобках, если UTF-8 неверно интерпретируется как последовательность 8-битных байтов.Думаю об этом.Если [nñm] неверно истолкован не как три логических символа, а как четыре физических байта, вы бы соответствовали только символу с кодовой точкой 6E или C3 или B1 или 6D.

Для некоторых целей вы можете избежатьпереписав [nñm] как (?:n|ñ|m).Это зависит только от того, что вы делаете.Обшивка не будет работать.

Кроме того, в Юникоде есть специальные правила обсадных колонок для турецких без точек. I.

Звучит так, будто PHP просто недостаточно современен.Вздох.

...