Регулярное выражение Perl застревает в нескольких экземплярах наборов символов

StackOverflow https://stackoverflow.com/questions/4250120

Вопрос

Я начал с сумасшедших неудач при использовании preg_replace в php и свел это к проблемному случаю, когда несколько классов символов используют вместе турецкие буквы «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 не подавиться этим?

Это было полезно?

Решение

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

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

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

Похоже, PHP просто недостаточно современен.Вздох.

Другие советы

Возможно, вам придется сообщить 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.Таким образом, входные данные Юникода, которые вы передаете, скорее всего, будут рассматриваться как строка байтов, в которую был закодирован Юникод.

Если вы можете быть уверены, что ваш ввод поступает в формате 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";

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top