4

我一开始在 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";

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

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

编辑:我正在尝试编程的语言问题的背景。

编辑 2:添加use utf8;指令确实修复了 perl 版本。由于我最初的问题是一个 php 程序,我只切换到 perl 来查看它是否是 php 中的一个错误,这对我没有多大帮助。有人知道使 PHP 不会因此而窒息的指令吗?

4

2 回答 2

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 形式输入的,那么您可以匹配 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";

这很清楚为什么前三个测试通过而最后一个测试失败。如果您使用开始/结束锚点^...$,我想您会发现只有第一个测试通过。

于 2010-11-22T20:55:17.993 回答
4

如果 UTF-8 被错误解释为 8 位字节序列,则多字节序列不会在括号中的 char 类中执行您想要的操作。想想看。如果[nñm]它不是三个逻辑字符而是四个物理字节,则只能匹配代码点为 6E 或 C3 或 B1 或 6D 的字符。

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

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

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

于 2010-11-22T21:54:37.507 回答