如何匹配 perl 正则表达式 (regexp) 中的重音字符和波浪字符?

Posted

技术标签:

【中文标题】如何匹配 perl 正则表达式 (regexp) 中的重音字符和波浪字符?【英文标题】:How do you match accented and tilde characters in a perl regular expression (regexp)? 【发布时间】:2011-07-06 15:23:47 【问题描述】:

用户输入一组带有重音符号和波浪线的名称:

Renato Núñez, David DeJesús, and Edwin Encarnación 

我的数据库中有这些人的英文名字

@names = ('Renato Nunez','David DeJesus','Edwin Encarnacion');

我希望对这些名称进行正则表达式匹配。

$string = "Renato Núñez, David DeJesús, and Edwin Encarnación";
foreach my $name (@names) 
    print "found:$name\n" if ($name =~ /$string/);

目前我没有找到匹配项。

我试过了,但是没用。

$string = "Renato Núñez, David DeJesús, and Edwin Encarnación";
foreach my $name (@names) 
    $name =~ s|a|[áa]|;
    $name =~ s|e|[ée]|;
    $name =~ s|i|[íi]|;
    $name =~ s|o|[óo]|;
    $name =~ s|u|[úu]|;
    $name =~ s|n|[ñn]|;
    # Originally: print "found:$name\n" if ($name =~ /$string/);
    # Corrected to:
    print "found:$name\n" if ($string =~ /$name/);

编辑:抱歉,我在最后一行中颠倒了 $name 和 $string。

有什么建议吗?

【问题讨论】:

建议一:perldoc perlop中的正则表达式操作符。我想你想说$string =~ /$name/而不是$name =~ /$string/,和s|[áa]|a|而不是s|a|[áa]| 我有s|||以我的方式排序,因为我想从字符串“David DeJesus”构建一个正则表达式,它将匹配带有或不带有重音的名称。 哦,现在我明白了。您正在尝试在 $name 中构建正则表达式,而不是去除未英语化的字符。 查看我刚刚添加到答案中的代码,向您展示如何匹配其中可能包含重音符号的字符串,而不必担心它们是否存在。 【参考方案1】:

看来你交换了参数。 你输入

$name =~ s|a|[áa]|;

尝试将模式“a”替换为“[áa]” 试试

$name =~ s|[áa]|a|;

交换匹配,它会起作用。

$string = "Renato Núñez, David DeJesús, and Edwin Encarnación";
foreach my $name (@names) 
    print "found:$name\n" if ($string =~ /$name/);

    Unicode 正则表达式从 perl 5.6 开始在 perl 中工作: http://www.regular-expressions.info/unicode.html 您是否检查了源代码编码(latin1 或 utf8)中的数据库编码。

【讨论】:

是的,我在最后一个命令中颠倒了 $string 和 $name。但我不认为反转 s|[aa']|a|会起作用,因为这会将我的名字转换为非西班牙语字符,并且与字符串不匹配?【参考方案2】:

我相信您正在使用字符串“Renato Núñez, David DeJesús, and Edwin Encarnación”作为正则表达式

如果我理解正确,您是在尝试匹配短语“Renato Núñez、David DeJesús 和 Edwin Encarnación”中的每一个名字。

如果是这样,那么你需要写:$string =~ /$name/ instead of $name =~ /$string/

【讨论】:

好的,谢谢。我把它倒过来了。我现在已经解决了这个问题。【参考方案3】:

这可能更符合您的尝试。

use strict;
use warnings;

my @AngloNames = ('Renato Nunez','David DeJesus','Edwin Encarnacion');
my @AngEthRx;

for my $val (@AngloNames) 
   $_ = $val;
   s/a/[áa]/g;
   s/e/[ée]/g;
   s/i/[íi]/g;
   s/o/[óo]/g;
   s/u/[úu]/g;
   s/n/[ñn]/g;
   push @AngEthRx, $_;


# User input query string ...
my $AngEthQuery = "Renato Núñez, David DeJesús, and Edwin Encarnación";

for my $i (0 .. $#AngEthRx) 
   if ( $AngEthQuery =~ /($AngEthRx[$i])/ ) 
      print "found: $AngloNames[$i] ~ $1\n";
   

出来

found: Renato Nunez ~ Renato Núñezfound: David DeJesus ~ David DeJesúsfound: Edwin Encarnacion ~ Edwin Encarnación

【讨论】:

我会试试这个。这看起来像我之前没有工作的东西。这是通过网络表单发送的,因此字符串中的字符可能正在以某种方式进行编码。我得检查一下。【参考方案4】:

谷歌搜索,我发现这个问题很常见(我使用了查询“perl remove diacritic”)。请记住,这不是一门“精确”的科学(删除变音符号和将文本英语化)。这里有一些链接:

http://www.ahinea.com/en/tech/accented-translate.html

http://search.cpan.org/~wollmers/Text-Undiacritic-0.02/lib/Text/Undiacritic.pm

http://search.cpan.org/~ldachary/Text-Unaccent-1.08/Unaccent.pm

作为一个建议,对于快速 n-dirty 方法:

以规范化形式 D 规范化字符串(参见 http://perldoc.perl.org/5.8.9/Unicode/Normalize.html )。例如,这会将 ''è'' 更改为 ''e'' + '' ̀ ''(组合坟墓,U+0300)。 用空字符串替换所有标记(它是一个 Unicode 类)。正则表达式基于\pM(它将找到所有标记) 现在您的字符串没有带变音符号的符号,您可以进行“简单”比较 但请注意,许多“奇怪的字母”幸存下来:例如 ßØœ。这是一个快速n-肮脏的n结束!

我无法为您提供更多帮助,因为我已经多年没有使用 Perl 编程了。

【讨论】:

【参考方案5】:
use Unicode::Normalize;
($gutted = NFD($string)) =~ s/pM//g;

但是,这几乎总是错误的。你打算怎么办

Ævar Arnfjörð Dženan Ljubović King Henry Ⅷ Carlos Ⅴº, el Emperador

只要拥抱 Unicode。 匹配有或没有变音符号的正确方法是实例化一个 Unicode::Collator 对象,其强度设置为忽略变音符号。然后只需调用cmpeq 方法即可。

编辑

是你应该如何去做这些事情。证人:

«La Alberguería de Argañán»    sí tiene /AN/ en  un par de sitios «añ» y «án»
                               sí tiene /AL/ en     un solo sitio «Al»
«Bóveda del Río Almar»         sí tiene /AL/ en     un solo sitio «Al»
«Cabezón de Liébana»           sí tiene /AN/ en     un solo sitio «an»
                               sí tiene /ON/ en     un solo sitio «ón»
«Doña Mencía»                  sí tiene /EN/ en     un solo sitio «en»
                               sí tiene /ON/ en     un solo sitio «oñ»
«Gallegos de Argañán»          sí tiene /AN/ en  un par de sitios «añ» y «án»
                               sí tiene /AL/ en     un solo sitio «al»
«Griñón»                       sí tiene /IN/ en     un solo sitio «iñ»
                               sí tiene /ON/ en     un solo sitio «ón»
«Logroño»                      sí tiene /ON/ en     un solo sitio «oñ»
«Lliçà d’Amunt»                sí tiene /UN/ en     un solo sitio «un»
«Madroñal»                     sí tiene /ON/ en     un solo sitio «oñ»
                               sí tiene /AL/ en     un solo sitio «al»
«Mantilla»                     sí tiene /AN/ en     un solo sitio «an»
«Mañón»                        sí tiene /AN/ en     un solo sitio «añ»
                               sí tiene /ON/ en     un solo sitio «ón»
«Matilla de los Caños del Río» sí tiene /AN/ en     un solo sitio «añ»
«Montalbán de Córdoba»         sí tiene /AN/ en     un solo sitio «án»
                               sí tiene /ON/ en     un solo sitio «on»
                               sí tiene /AL/ en     un solo sitio «al»
«La Peña»                      sí tiene /EN/ en     un solo sitio «eñ»
«Piñuécar–Gandullas»           sí tiene /AN/ en     un solo sitio «an»
                               sí tiene /IN/ en     un solo sitio «iñ»
«A Pobra do Caramiñal»         sí tiene /IN/ en     un solo sitio «iñ»
                               sí tiene /AL/ en     un solo sitio «al»
«Prats de Lluçanès»            sí tiene /AN/ en     un solo sitio «an»
«Ribamontán al Monte»          sí tiene /AN/ en     un solo sitio «án»
                               sí tiene /ON/ en  un par de sitios «on» y «on»
                               sí tiene /AL/ en     un solo sitio «al»
«La Roca del Vallès»           sí tiene /AL/ en     un solo sitio «al»
«San Martín del Castañar»      sí tiene /AN/ en  un par de sitios «an» y «añ»
                               sí tiene /IN/ en     un solo sitio «ín»
«Santa Eulàlia de Ronçana»     sí tiene /AN/ en  un par de sitios «an» y «an»
                               sí tiene /ON/ en     un solo sitio «on»
                               sí tiene /AL/ en     un solo sitio «àl»
«Santa María de Cayón»         sí tiene /AN/ en     un solo sitio «an»
                               sí tiene /ON/ en     un solo sitio «ón»
«Valverde de Alcalá»           sí tiene /AL/ en          3 sitios «al», «Al» y «al»
«Villar de Argañán»            sí tiene /AN/ en  un par de sitios «añ» y «án»

这是生成它的代码。

#!/usr/bin/env perl
#
# búsqueda-libre:
#
#    Cómo se debiera ordenar y buscar palabras en Unicode
#    que pueden llevarse marcas diacríticas (o no) sin que
#    éstas afecten la búsqueda.  También cómo cambiar el
#    el orden para que no cuente con articulos al principio
#    del los nombres, como se hace con los títulos de libros &c.
#
# Tom Christiansen <tchrist@perl.com>
# Fri Mar  4 21:06:35 MST 2011
#
#############################################

use utf8;
use 5.10.1;
use strict;
use warnings; # FATAL => "all";
use autodie;
use charnames qw< :full >;

use List::Util qw< max first >;
use Unicode::Collate;

my $INCLUÍR_NINGUNOS               = 0;
my $SI_IMPORTAN_MARCAS_DIACRÍTICAS = 0;

sub sí_ó_no(_)  $_[0] ? "sí" : "no" 

sub encomillar(_) 
    return join $_[0] =>
        "\NLEFT-POINTING DOUBLE ANGLE QUOTATION MARK",
        "\NRIGHT-POINTING DOUBLE ANGLE QUOTATION MARK",
    ;


binmode(STDOUT, ":utf8");
# Ésta está demasiada larga para la pantalla. :(
#
#    La Ciudad de Nuestra Señora la Reina de Los Ángeles de Porciúncula, California Alta
#

my @ciudades_españolas = ordenar_a_la_española(<<'LA_ÚLTIMA' =~ /\S.*\S/g);
        Santa Eulàlia de Ronçana
        Mañón
        A Pobra do Caramiñal
        La Alberguería de Argañán
        Logroño
        La Puebla del Río
        Villar de Argañán
        Piñuécar–Gandullas
        Mantilla
        Gallegos de Argañán
        Madroñal
        Griñón
        Lliçà d’Amunt
        Valverde de Alcalá
        Montalbán de Córdoba
        San Martín del Castañar
        La Peña
        Cabezón de Liébana
        Doña Mencía
        Santa María de Cayón
        Bóveda del Río Almar
        La Roca del Vallès
        Matilla de los Caños del Río
        Prats de Lluçanès
        Ribamontán al Monte
LA_ÚLTIMA

my $cmáx = -(2 + max map  length  @ciudades_españolas);

my @búsquedas = < A,E,I,O,UN AL >;
my $bmáx = -(2 + max map  length  @búsquedas);

my $ordenador = new Unicode::Collate::
                    level           => $SI_IMPORTAN_MARCAS_DIACRÍTICAS ? 2 : 1,
                 ## variable        => "non-ignorable",  # blanked, non-ignorable, shifted, shift-trimmed
                    normalization   => undef,
                ;

for my $aldea (@ciudades_españolas) 
    my $déjà_imprimée;
    for my $búsqueda (@búsquedas) 
        my @resultados = $ordenador->gmatch($aldea, $búsqueda);
        next unless @resultados || $INCLUÍR_NINGUNOS;
        printf qq(%*s %s tiene %*s en %17s %s\n),
                $cmáx => !$déjà_imprimée++ && encomillar($aldea),
                sí_ó_no(@resultados),
                $bmáx => "/$búsqueda/",
                cuántos_sitios(@resultados),
                enfilar(@resultados);
    


sub cuántos_sitios 
    my @lista = @_;
    my $cantidad = @_;
    given ($cantidad) 
        when (0)   return    "ningún sitio"    
        when (1)   return   "un solo sitio"    
        when (2)   return "un par de sitios"   
        default    return "$cantidad sitios"   
    


sub enfilar 
    my @lista = map  encomillar  @_;

    my $separador  = "\NCOMMA";
       $separador  = "\NSEMICOLON"   if first  /$separador/  @lista;
       $separador .= "\NSPACE";

    given (scalar @lista) 
        when (0)   return ""                       
        when (1)   return "@lista"                 
        when (2)   return join " y " => @lista     
        default    return
            join($separador  => @lista[ 0 .. ($#lista-1) ])
                     . " y $lista[$#lista]";
        
    


###################################################
# Para ordenar los elementos de la lista
# en el estilo tradicional del castellano.
#
# Tenemos en cuenta que sí pueden aparecerse nombres
# de ciudades que no son nombres sólo castellanos
# sino tambíen catalanes y gallegos — y tal vez más,
# como en asturianu or aranés, pero no he pensado
# mucho es estos.
###################################################

sub ordenar_a_la_española 
    my @lista = @_;

    state $ordenador_a_la_española = new Unicode::Collate::

        # Si se tuviese Unicode::Collate::Locale con "es__traditional",
        # no haría falta este primer lío con su entrada especial,
        # con la excepción de la c-cedilla, la cual aquí se ordena
        # como si fuese catalán, no castellano.

        # Vamos a meter las nuevas entradas después de éstas,
        # que son copiadas del DUCET v6.0.0.  Tuve que cambiar unos
        # valores que tenía este código desde otra versión anterior.
        #
        # 0043  ; [.123D.0020.0008.0043] # LATIN CAPITAL LETTER C
        # 00C7  ; [.123D.0020.0008.0043][.0000.0056.0002.0327] # LATIN CAPITAL LETTER C WITH CEDILLA; QQCM
        # 004C  ; [.1330.0020.0008.004C] # LATIN CAPITAL LETTER L
        # 004E  ; [.136D.0020.0008.004E] # LATIN CAPITAL LETTER N
        # 00D1  ; [.136D.0020.0008.004E][.0000.004E.0002.0303] # LATIN CAPITAL LETTER N WITH TILDE; QQCM

        entry => <<'SALIDA',   # :)

               00E7      ; [.123E.0020.0002.0327] # c-cedilla
               0063 0327 ; [.123E.0020.0002.0327] # c-cedilla
               00C7      ; [.123E.0020.0002.0327] # C-cedilla
               0043 0327 ; [.123E.0020.0002.0327] # C-cedilla

               0063 0068 ; [.123F.0020.0002.0043] # ch
               0043 0068 ; [.123F.0020.0007.0043] # Ch
               0043 0048 ; [.123F.0020.0008.0043] # CH

               006C 006C ; [.1331.0020.0002.004C] # ll
               004C 006C ; [.1331.0020.0007.004C] # Ll
               004C 004C ; [.1331.0020.0008.004C] # LL

               00F1      ; [.136E.0020.0002.0303] # n-tilde
               006E 0303 ; [.136E.0020.0002.0303] # n-tilde
               00D1      ; [.136E.0020.0008.0303] # N-tilde
               004E 0303 ; [.136E.0020.0008.0303] # N-tilde

SALIDA

       upper_before_lower => 1,

       normalization => "NFKD",  # ¿Y porqué no?

       preprocess => sub 
           my $_ = shift;

       ###
       # no incluye los artículos definitivos ni indefinitivos
       ###

           s/^L\pQMARK//;    # puede encontrarse en el catalán

           s ^

             (?:         # del castellano
                 El
               | Los
               | La
               | Las
                         # del catalán
               | Els
               | Les
               | Sa
               | Es
                         # del gallego
               | O
               | Os
               | A
               | As
             )

             \h +

          x;

        # Luego quita las palabras no-importantes interiores.

           s/\b[dl]\pQMARK//g;   # del catalán

           s
               \b
               (?:
                   el  | los | la | las | de  | del | y          # ES
                 | els | les | i  | sa  | es  | dels             # CA
                 | o   | os  | a  | as  | do  | da | dos | das   # GAL
               )
               \b
           gx;

          return $_;

       ,   # fin de rutina preprocesadora

  ## ¡Fijaos que no borréis esta marca!
  ##     Este punto y coma marca el fin
  ##     de los argumentos del constructor
  ##     empezado ya muchas lineas arriba.
  ##   ˅
       ;  # ←←← Sí, ése — dejadlo en paz o muy tristes os quedaréis.
  ##   ˄

    return $ordenador_a_la_española->sort(@lista);

【讨论】:

谢谢。我会更仔细地看看这个。

以上是关于如何匹配 perl 正则表达式 (regexp) 中的重音字符和波浪字符?的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 正则表达式

MySQL 正则表达式

MySQL 正则表达式

JavaScript之RegExp对象

吴裕雄 21-MySQL 正则表达式

数据库教程MySQL 正则表达式