Pergunta

Comecei com algumas falhas loucas usando preg_replace no PHP e o resumi no caso problemático de ter mais de uma classe de personagem usando pontilhado "i" e não usado "ı" juntos. Aqui está um caso de teste simples no 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";
?>

E o mesmo caso de teste novamente em 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";

Os três primeiros testes funcionam bem. O último não corresponde.

Por que isso funciona bem como uma classe de personagem uma vez, mas não a segunda vez na mesma expressão? Como escrevo uma expressão para corresponder a uma palavra como essa que precisa corresponder, independentemente das combinações de letras com as quais está escrito?

Editar: Plano de fundo o problema do idioma Estou tentando programar.

Editar 2: Adicionando a use utf8; A diretiva corrige a versão Perl. Como meu problema original estava com um programa PHP e eu só mudei para o Perl para ver se era um bug no PHP, isso não me ajuda muito. Alguém conhece a diretiva para fazer PHP não sufocar isso?

Foi útil?

Solução

As seqüências multibytes não farão o que você deseja nas classes de char entre colchetes se o UTF-8 estiver sendo mal interpretado como uma sequência de bytes de 8 bits. Pense nisso. Se [nñm] é mal interpretado não como três caracteres lógicos, mas como quatro bytes físicos, você corresponderia apenas a um personagem cujo ponto de código é 6E ou C3 ou B1 ou 6D.

Para alguns propósitos, você pode se safar de reescrever [nñm] Como (?:n|ñ|m). Apenas depende do que você está fazendo. O material da carcaça não funciona.

Além disso, o Unicode possui regras especiais de invólucro para um ponto de ponta turco i.

Parece que o PHP simplesmente não é moderno o suficiente. Suspirar.

Outras dicas

Pode ser necessário informar ao Perl que seu arquivo de origem contém caracteres UTF8. Tentar:

#!/usr/bin/perl

use utf8;   # **** Add this line

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

O que não o ajuda com PHP, mas pode haver uma diretiva semelhante no PHP. Caso contrário, tente usar alguma forma de sequência de fuga para evitar colocar o caráter literal no seu código de origem. Não sei nada sobre PHP, então não posso evitar isso.

Editar
Estou lendo que o PHP não tem suporte Unicode. Portanto, a entrada Unicode que você passa é provavelmente tratada como a sequência de bytes de que o unicode foi codificado como.

Se você pode ter certeza de que sua entrada está chegando como UTF-8, você pode corresponder à sequência UTF-8 para ı qual é \xc4 \xb1 como em:

$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";

Isso funciona?

Editar novamente:
Posso explicar por que seus três primeiros testes passam. Vamos fingir que em sua codificação, ı é codificado como ABCDE. Então o PHP vê o seguinte:

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";

O que torna óbvio por que os três primeiros testes passam e o último falha. Se você usar uma âncora de início/final ^...$ Eu acho que você descobrirá que apenas o primeiro teste passa.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top