12 Stimmen

Wie passt man Akzent- und Tilde-Zeichen in einem regulären Ausdruck (regexp) in Perl an?

Ein Benutzer gibt eine Reihe von Namen mit Akzenten und Tilden ein:

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

Meine Datenbank enthält anglisierte Namen für diese Personen

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

Ich möchte einen Regexp-Abgleich für diese Namen durchführen.

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

In der derzeitigen Form erhalte ich keine Treffer.

Ich habe das versucht, aber es hat nicht funktioniert.

$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/);
}

EDIT: Entschuldigung, ich hatte $name und $string in der letzten Zeile vertauscht.

Irgendwelche Vorschläge?

2 Stimmen

Vorschlag 1: Operatoren für reguläre Ausdrücke in perldoc perlop . Ich glaube, Sie wollen sagen $string =~ /$name/ anstelle von $name =~ /$string/ y s|[áa]|a| anstelle von s|a|[áa]| .

1 Stimmen

Ich habe die s|||-Reihenfolge so gewählt, weil ich eine Regex aus der Zeichenkette "David DeJesus" erstellen möchte, die den Namen mit oder ohne Akzent findet.

0 Stimmen

Oh, jetzt verstehe ich. Sie versuchen, einen regulären Ausdruck zu erstellen in $name nicht, um die nicht anglisierten Zeichen zu entfernen.

8voto

tchrist Punkte 76479
use Unicode::Normalize;
($gutted = NFD($string)) =~ s/pM//g;

Dies ist jedoch fast immer der falsche Weg. Was werden Sie tun, um

  • Ævar Arnfjörð
  • enan ubovi
  • King Henry
  • Carlos º, el Emperador

Machen Sie sich Unicode einfach zu eigen. Die die richtige Art und Weise, Dinge mit oder ohne diakritische Zeichen zu verbinden ist die Instanziierung einer Unicode::Collator Objekt, bei dem die Stärke so eingestellt ist, dass diakritische Zeichen ignoriert werden. Dann rufen Sie einfach das cmp o eq Methoden.

EDITAR

Diese ist die Art und Weise, wie Sie diese Dinge angehen sollten. Zeuge:

«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»

Und hier ist der Code, der das erzeugt.

#!/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 fese 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);
}

0 Stimmen

Vielen Dank dafür. Ich werde mir das genauer ansehen.

2voto

xanatos Punkte 105813

Wenn man danach googelt, sieht man, dass das Problem recht häufig auftritt (ich habe die Abfrage "perl remove diacritic" verwendet). Denken Sie daran, dass es sich nicht um eine "exakte" Wissenschaft handelt (Entfernen von diakritischen Zeichen und Anglisieren von Text). Hier gibt es einige Links:

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

Ein Vorschlag für eine schnelle und einfache Methode:

  • Normalisieren Sie die Zeichenkette in Normalisierungsform D (siehe dies http://perldoc.perl.org/5.8.9/Unicode/Normalize.html ). Dadurch wird z. B. das ''è'' zu ''e'' + '' '' (das kombinierende Grab, U+0300).
  • Ersetzen Sie alle Marks (es handelt sich um eine Unicode-Klasse) durch string empty. Die Regex basiert auf der \p{M} (es werden alle Markierungen gefunden)
  • Jetzt enthält die Zeichenkette keine diakritischen Zeichen, und Sie können einen "einfachen" Vergleich durchführen
  • Es ist jedoch zu beachten, dass viele "seltsame Buchstaben" überlebt haben: ß zum Beispiel. Das war ein Schnellschuss zum Schluss!

Ich kann Ihnen nicht weiterhelfen, da ich seit vielen Jahren nicht mehr in Perl programmiere.

1voto

VGE Punkte 4089

Es scheint, dass Sie die Parameter vertauschen. Sie tippen

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

die versuchen, das Muster "a" durch "[áa]" zu ersetzen Versuchen Sie

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

Tauschen Sie das Streichholz aus und es wird funktionieren.

$string = "Renato Núñez, David DeJesús, and Edwin Encarnación";
foreach my $name (@names) {
    print "found:$name\n" if ($string =~ /$name/);
}
  1. Unicode reguläre Ausdrücke funktionieren in Perl seit Perl 5.6: http://www.regular-expressions.info/unicode.html
  2. Haben Sie die Datenbankkodierung und die Kodierung Ihres Quellcodes überprüft (latin1 oder utf8).

0 Stimmen

Ja, ich hatte $string und $name im letzten Befehl vertauscht. Aber ich glaube nicht, dass die Umkehrung von s|[aa']|a| funktionieren wird, da dies meine Namen in nicht-spanische Zeichen umwandelt und die Zeichenfolge nicht übereinstimmen wird?

1voto

jmatias Punkte 89

Ich glaube, Sie verwenden als Regexp die Zeichenfolge "Renato Núñez, David DeJesús und Edwin Encarnación".

Wenn ich es richtig verstehe, versuchen Sie, jeden einzelnen Namen innerhalb des Satzes "Renato Núñez, David DeJesús und Edwin Encarnación" zu finden.

Wenn das der Fall ist, dann müssen Sie schreiben: $string =~ /$name/ instead of $name =~ /$string/

1 Stimmen

Ja, ich danke Ihnen. Ich hatte es umgedreht. Ich habe die Frage jetzt korrigiert.

1voto

Dies entspricht vielleicht eher dem, was Sie zu tun versuchen.

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

aus

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

0 Stimmen

Ich werde dies versuchen. Das sah so aus wie das, was ich hatte und was nicht funktionierte. Dies ist immer über ein Webformular gesendet, so vielleicht die Zeichen in der Zeichenfolge werden irgendwie verschlüsselt. Ich werde das überprüfen müssen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X