Perl正则表达式阻塞了多个字符集实例

时间:2010-11-22 20:49:15

标签: php regex perl unicode turkish

我开始在php中使用preg_replace时出现了一些疯狂的失败,并将其归结为使用土耳其语点缀“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";

前三个测试工作正常。最后一个不匹配。

为什么这个作为一个字符类工作正常但不是第二次在同一个表达式中?我如何编写一个表达式来匹配这样一个需要匹配的单词,无论它写的是什么字母组合?

编辑: the language problem上的背景我正在尝试编程。

编辑2:添加use utf8;指令确实修复了perl版本。由于我最初的问题是使用php程序而且我只切换到perl以查看它是否是php中的错误,这对我没有多大帮助。 是否有人知道使PHP不会出现这种情况的指令?

2 个答案:

答案 0 :(得分:8)

您可能需要告诉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中可能有类似的指令。否则,请尝试使用某种形式的转义序列,以避免将文字字符放入源代码中。我对PHP一无所知,所以我无能为力。

<强> 修改
我读到PHP没有Unicode支持。因此,您传递的unicode输入可能被视为unicode编码为的字节串。

如果您可以确信您的输入是以utf-8形式输入的,那么您可以匹配ı的{​​{1}}的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"; 被编码为ı。然后PHP看到以下内容:

ABCDE

这使得前三个测试通过并且最后一个测试失败的原因显而易见。如果您使用开始/结束锚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"; ,我认为您会发现只有第一次测试通过。

答案 1 :(得分:4)

如果UTF-8被误解为8位字节序列,则多字节序列将无法在括号内的char类中执行所需操作。想一想。如果[nñm]被误解为不是三个逻辑字符而是四个物理字节,则只匹配代码点为6E或C3或B1或6D的字符。

出于某些目的,您可能会将[nñm]重写为(?:n|ñ|m)。这取决于你在做什么。外壳的东西不起作用。

此外,Unicode对土耳其无点i有特殊的大小写规则。

听起来像PHP还不够现代。叹息。