【问题标题】:How do you match accented and tilde characters in a perl regular expression (regexp)?如何匹配 perl 正则表达式 (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 中构建正则表达式,而不是去除未英语化的字符。
  • 查看我刚刚添加到答案中的代码,向您展示如何匹配其中可能包含重音符号的字符串,而不必担心它们是否存在。

标签: regex perl unicode localization


【解决方案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/);
}
  1. Unicode 正则表达式从 perl 5.6 开始在 perl 中工作: http://www.regular-expressions.info/unicode.html
  2. 您是否检查了源代码编码(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úñez
found: David DeJesus ~ David DeJesús
found: 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 类)。正则表达式基于\p{M}(它将找到所有标记)
  • 现在您的字符串没有带变音符号的符号,您可以进行“简单”比较
  • 但请注意,许多“奇怪的字母”幸存下来:例如 ߨœ。这是一个快速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] =>
            "\N{LEFT-POINTING DOUBLE ANGLE QUOTATION MARK}",
            "\N{RIGHT-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,U}N 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  = "\N{COMMA}";
           $separador  = "\N{SEMICOLON}"   if first { /$separador/ } @lista;
           $separador .= "\N{SPACE}";
    
        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\p{QMARK}//;    # 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]\p{QMARK}//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);
    }
    

    【讨论】:

    • 谢谢。我会更仔细地看看这个。
    猜你喜欢
    • 2012-06-22
    • 1970-01-01
    • 2015-07-27
    • 1970-01-01
    • 1970-01-01
    • 2013-07-03
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    相关资源
    最近更新 更多