使用Perl匹配带重音符号的字符问题

4

这段代码:

perl -pe 's/^(\D\w+ \w+)( word )/\1;word/gi'

当输入包含带重音或特殊字符的单词时,代码将无法正常工作,例如:á,Ș。

细节说明:

我有这段代码来计算唯一艺术家文件的数量。

find /PATH/ -type f -exec basename "{}" + 2>/dev/null |

perl -pe 's/ - .*//g' | LC_ALL=C  sort -f | uniq -c -i|

gsed -e 's/$/;/'|

awk '{numero=$1;$1=""}{print $0,numero}'|

perl -pe 's/^(\D\w+ \w+)( & )/\1;&/g' | 
perl -pe 's/^(\D\w+ \w+ \w+)( & >)/\1;&/g' | 
perl -pe 's/^(\D\w+ \w+ \w+ \w+)( & )/\1;&/g' | 
perl -pe >'s/^(\D\w+ \w+ \w+ \w+ \w+)( & )/\1;&/g' |

perl -pe 's/^(\D\w+ \w+)( Con )/\1;Con/gi' | 
perl -pe 's/^(\D\w+ \w+ >\w+)( Con )/\1;Con/gi' | 
perl -pe 's/^(\D\w+ \w+ \w+ \w+)( Con >)/\1;Con/gi' |  
perl -pe 's/^(\D\w+ \w+ \w+ \w+ \w+)( Con )/\1;Con/gi'|

perl -pe 's/^(\D\w+ \w+)( Și )/\1;Și/gi' | 
perl -pe 's/^(\D\w+ \w+ \w+)( >Și )/\1;Și/gi' | 
perl -pe 's/^(\D\w+ \w+ \w+ \w+)( Și )/\1;Și/gi' | 
perl >-pe 's/^(\D\w+ \w+ \w+ \w+ \w+)( Și )/\1;Și/gi'| > /PATH/File.txt

我有这些文件:
Betty Curtis & Orchestra - 歌曲标题
Betty Curtis Con Johnny Dorelli - 歌曲标题
Betty Curtis - 歌曲标题
Margareta Pâslaru - 歌曲标题
Margareta Pâslaru & Grup - 歌曲标题
Margareta Pâslaru Și Sincron - 歌曲标题
Matilde Sánchez - 歌曲标题
Matilde Sánchez Con El Mariachi Vargas De Tecalitlán - 歌曲标题
期望输出结果为:
Betty Curtis; 3
Margareta Pâslaru; 3
Matilde Sánchez; 2
而实际输出结果是:
Betty Curtis; 3
Margareta Pâslaru; 1
Margareta Pâslaru & Grup; 1
Margareta Pâslaru Și Sincron; 1
Matilde Sánchez; 1
Matilde Sánchez Con El Mariachi Vargas De Tecalitlán; 1

确实,这段代码非常复杂(整个脚本共有19行......)。 规则是如果名称中有连词或括号,则截断名称,除非该名称由单个单词组成。 如果没有连词或括号,则完整保存名称。

例如:“Gervis Quebodeaux Rayne Serenaders”仍然是“Gervis Quebodeaux Rayne Serenaders”;

我想压缩“Perl-pe”部分:(D w + w +),(D w + w + w +)等...很无聊。 但我不知道该如何做到。

我必须在概述和尽可能保留更多信息之间找到平衡点。

目前我有30个案例(规则),除了“&”以外还有世界上许多语言中的“With”“Con”“e”“Y”“Et”“Und”等等。

该脚本运行良好,但不能处理带有重音和特殊字母的名称

该脚本的工作原理如下:

例如,我有许多Duke Ellington的文件,其中有许多不同的历史标题。

杜克·艾灵顿:2个文件 杜克·艾灵顿和棉花俱乐部管弦乐团:3 杜克·艾灵顿和他的著名管弦乐团:7 杜克·艾灵顿和他的著名管弦乐团;(与本·韦伯斯特一起):4 杜克·艾灵顿和他的著名管弦乐团;(与约翰尼·霍奇斯一起):3 杜克·艾灵顿和他的乐团:129 杜克·艾灵顿和他的乐团(与本·韦伯斯特一起):14 杜克·艾灵顿和他的乐团(与约翰尼·霍奇斯一起):8 杜克·艾灵顿和他的乐团(pn.):2 杜克·艾灵顿和他的乐团(v. Al Hibble):1 杜克·艾灵顿和他的乐团(v. Al Hibbler):1 杜克·艾灵顿和他的乐团(v. Herb Jeffries):9 杜克·艾灵顿和他的乐团(v. Ozzie Bailey):1 杜克·艾灵顿和他的乐团(v. Ozzie Bailey,Ray Nance Vln.):1 杜克·艾灵顿和他的乐团;(v. Ray Nance?):1 杜克·艾灵顿和他的乐团;(v.M):1 杜克·艾灵顿(与节奏男孩(2°c Bing Crosby,Al Rinker和Harry Barris)一起):1 杜克·艾灵顿(与节奏男孩(Bing Crosby,Al Rinker和Harry Barris)一起):1 杜克·艾灵顿(v. Dick Robertson):1 杜克·艾灵顿与贝西伯爵:3 杜克·艾灵顿与杰拉尔德·威尔逊:13 杜克·艾灵顿的太空人:1 杜克·艾灵顿的华盛顿乐团:1
通过生成此文件的脚本的工作
杜克·艾灵顿; 2
杜克·艾灵顿;&棉花俱乐部管弦乐队; 3
杜克·艾灵顿;&他的著名乐团; 7
杜克·艾灵顿;&他的著名乐团;(ft.本·韦伯斯特); 4
杜克·艾灵顿;&他的著名乐团;(ft.约翰尼·霍奇斯); 3
杜克·艾灵顿;&他的乐团; 129
杜克·艾灵顿;&他的乐团;(ft.本·韦伯斯特); 14
杜克·艾灵顿;&他的乐团;(ft.约翰尼·霍奇斯); 8
杜克·艾灵顿;&他的乐团;(pn.); 2
杜克·艾灵顿;&他的乐团;(v.阿尔·希布尔); 1
杜克·艾灵顿;&他的乐团;(v.赫伯·杰弗里斯); 9
杜克·艾灵顿;&他的乐团;(v.奥兹贝·贝利); 1
杜克·艾灵顿;&他的乐团;(v.奥兹贝·贝利,雷·南斯小提琴); 1
杜克·艾灵顿;&他的乐团;(v.雷·南斯?); 1
杜克·艾灵顿;&他的乐团;(v.M); 1
杜克·艾灵顿;(ft.节奏男孩(2°c 金·克劳斯比,阿尔·林克尔和哈里·巴里斯)); 1
杜克·艾灵顿;(ft.节奏男孩(金·克劳斯比,阿尔·林克尔和哈里·巴里斯)); 1
杜克·艾灵顿;(v.迪克·罗伯逊); 1
杜克·艾灵顿;与贝西共同演出; 3
杜克·艾灵顿;与杰拉尔德·威尔逊共同演出; 13
杜克·艾灵顿;太空人; 1
杜克·艾灵顿;华盛顿人; 1

这是输出结果:

杜克·艾灵顿:208

代码已完成: https://www.sendspace.com/file/dlep9q


“_doesn't work_”是什么意思?如果那一行是你的完整代码,它根本没有设置为支持Unicode。另外,你如何获取输入? - zdim
我已经放置了代码的必要部分。 - manub
谢谢您的更新。一个问题:在“_我有这些文件:_”之后的文本——那是一个文件吗?您想要从中计算艺术家的数量吗?这就是整个工作吗?(您展示的代码太过复杂了。) - zdim
每行的名称总是由前两个单词组成吗? - zdim
我回复你了。我更新了问题,因为我需要更多字符。 - manub
谢谢,这是一个非常困难的问题。 (1) 让您的代码正确处理Unicode并不难,这是最初的问题,也是我的回答所解决的。(2) 正确解析自然语言中的名称非常困难,通常无法解决。但是,您已经有了如何截断字符串的标准,这有所帮助。但是,我不太理解您的规则,因此我只提供了一个基本框架。请参见编辑。 - zdim
1个回答

6
显示的一行代码没有启用任何Unicode支持。您至少需要为其设置输入/输出流,并且在脚本中我建议这样做。
use open qw(:std :encoding(UTF-8));

在一行代码中有开关,查看您需要的组合请参阅 -C 下的 perlrun。例如:
echo "á, Ș." | perl -CASD -wnE'@m = /\w+/g; say for @m'

打印

á
Ș

因此可以理解带有重音的字符。

此外,您可能需要使用\X(而不是\w)来匹配扩展的字形簇


此帖子可能与第一部分令人放心但可怕(且信息丰富)的内容相关。

文献: perlunitutperlunifaqperluniintro(例如其Unicode I/O),以及perlunicode。请准备好perluniprops。还有一个类似于烹饪书的perlunicook(请参见标准前言以开始),以及Encode

请注意,正则表达式本身是支持 Unicode 的。


问题已进行了编辑,添加了代码、示例输入及其处理方式以及指向完整程序的链接。例如添加了以下一些名称如何被确定的澄清:

规则是如果有连接词或括号,则截断名称,除非名称由单个词组成。如果没有连接词或括号,则保存名称全称

这意味着缩短的名称至少需要有两个单词长度,或者字符串不应被截断(如评论所澄清的那样)。这几乎完全绕过了解析自然语言中名称的非常困难的问题,因为“连接词”是要提供的。

为演示从该列表中使用一些内容(来自问题中链接的程序),

use warnings;
use strict;
use feature 'say';

use utf8;                            # for utf8 characters in this script
use open qw(:std :encoding(UTF-8));  # for standard streams

sub extract_name {
    my ($line) = @_;
    # Rule for extracting the name:
    #   Truncate at $cutoff phrase if there are at least two words before it
    #   (incomplete list of alternations for a demo, from linked program)
    my $cutoff = qr{\s+(?:-|&|And|Con|Și)(?:\s+|\z)};  # with spaces
    my $parens = qr{\s+\(};                            # no space after

    # If there is a cut-off phrase on the line, extract what's before it
    # If that is at least two words long, return it;
    #   otherwise, return the whole line 
    if ( my ($name) = $line =~ /(.*?)(?:$cutoff|$parens)/ ) {
        return $name if split(' ', $name) >= 2;
    }
    return $line;
}

my $file = shift // die "Usage: $0 file\n";

open my $fh, '<', $file or die "Can't open $file: $!";

my %name_count;
while (my $line = <$fh>) { 
    chomp $line;
    ++$name_count{ extract_name($line) };
}

say "$_; $name_count{$_}" for sort keys %name_count;

"conjunction"(截止短语)的正则表达式模式使用qr运算符进行构建,以便更轻松地处理。它只是给定连词的选择 (|),这里从链接的程序中挑选了几个。我将那些不需要尾随空格的内容分开成另一个模式,这里仅适用于括号。
尽管sort和 cmp可能会在Unicode上产生错误的结果,但对于打印的报告进行排序是个好主意;请参见此帖子以了解如何使用utf8正确排序。
我在问题中显示的输入测试了这一点,并添加了以下行:
Johnny & The Hurricanes
An Awesome Band (Unknown)
以便能够测试名称标准的细节。它打印出来:
An Awesome Band; 1
Betty Curtis; 3
Johny & The Hurricanes; 1
Margareta Pâslaru; 3
Matilde Sánchez; 2
强烈建议不要使用“一行”来完成这种复杂性的工作(当打包到命令行中时,我几乎无法使上述子程序正确解析和工作)。如果需要将行传输到该程序,请告诉我,我可以添加该功能。

关于代码:[perl -CSD -wnE' ++$name_count{ (/(\w+\s+\w+)/)[0] }; END { say "$; $name_count{$}" for sort keys %name_count } ' input] 我无法理解如何替换这段代码:[perl -pe 's/^(\D\X+ \X+)( Con )/\1;Con/gi' | perl -pe 's/^(\D\X+ \X+ \X+)( Con )/\1;Con/gi'| perl -pe 's/^(\D\X+ \X+ \X+ \X+)( Con )/\1;Con/gi'| perl -pe 's/^(\D\X+ \X+ \X+ \X+ \X+)( Con )/\1;Con/gi'|] - manub
@manub "我无法理解它是如何代替的" --- 我没有尝试替换你的代码。这个问题是想从文本中提取名称,所以我写了代码来实现。我想不到你真正需要像示例中那样复杂的代码,所以我试图为你提供一个更简单的方法。但我不知道你对于“名称”的定义规则是什么;这是一个普遍的难题。 - zdim
@manub 你说“如果名字有两个或更多单词,则删除连接词。” - 好的,很清楚...但是问题并没有完全说明(请仔细再读一遍)。在“通过生成此文件的脚本的工作”之后,您重复了前面的文本吗?(还是我遗漏了一些细节?)然后你给出了一个由两个单词组成的姓名的总数(“Duke Elington”)。我不明白。然后,您说“除“&”外还有30个案例(规则)”,然后卷起未格式化的短语,例如“e_`”(单个带空格的“e”),那是某种口音吗?请表述得更清晰。 - zdim
@manub (1) 是的,我看了完整的代码,我看到了两个单词的长管道(与问题不同?)。 (2) 啊,分号,我现在看到了;但是我不知道那是什么意思。中间结果? (3) 我先发了一条评论,然后它就消失了。(没关系,我重复了重要的内容) - zdim
我用 chmod + x 创建了一个脚本文件,但它不起作用。 - manub
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接