如何匹配 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úñ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 类)。正则表达式基于\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
对象,其强度设置为忽略变音符号。然后只需调用cmp
或eq
方法即可。
编辑
这是你应该如何去做这些事情。证人:
«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) 中的重音字符和波浪字符?的主要内容,如果未能解决你的问题,请参考以下文章