如何在perl中使用正则表达式计算文件中的中文单词?

时间:2011-01-06 03:19:41

标签: regex perl embedding cjk

我尝试使用perl代码来计算文件的中文单词,看起来有效,但没有得到正确的结果。非常感谢任何帮助。

错误讯息是

Use of uninitialized value $valid in concatenation (.) or string at word_counting.pl line 21, <FILE> line 21.
Total things  = 125, valid words = 

在我看来问题是文件格式。 “总的东西”是125,即字符串编号(125行)。最奇怪的部分是我的控制台正确显示了所有单独的中文单词而没有任何问题。已安装utf-8 pragma。

#!/usr/bin/perl -w
use strict;
use utf8;
use Encode qw(encode);
use Encode::HanExtra;

my $input_file = "sample_file.txt";
my ($total, $valid);
my %count;

open (FILE, "< $input_file") or die "Can't open $input_file: $!";

while (<FILE>) {
 foreach (split) { #break $_ into words, assign each to $_ in turn
 $total++;
 next if /\W|^\d+/;  #strange words skip the remainder of the loop
 $valid++;
 $count{$_}++;  # count each separate word stored in a hash
 ## next comes here ##
      }
   }

   print "Total things  = $total, valid words = $valid\n";
   foreach my $word (sort keys %count) {
      print "$word \t was seen \t $count{$word} \t times.\n";
   }

##---Data----
sample_file.txt

那天约二更时,只见封肃方回来,欢天喜地.众人忙问端的.他乃说道:"原来本府新升的太爷姓贾名化,本贯胡州人氏,曾与女婿旧日相交.方才在咱门前过去,因见娇杏那丫头买线, 所以他只当女婿移住于此.我一一将原故回明,那太爷倒伤感叹息了一回,又问外孙女儿,我说看灯丢了.太爷说:`不妨,我自使番役务必探访回来.'说了一回话, 临走倒送了我二两银子."甄家娘子听了,不免心中伤感.一宿无话.至次日, 早有雨村遣人送了两封银子,四匹锦缎,答谢甄家娘子,又寄一封密书与封肃,转托问甄家娘子要那娇杏作二房. 封肃喜的屁滚尿流,巴不得去奉承,便在女儿前一力撺掇成了,乘夜只用一乘小轿,便把娇杏送进去了.雨村欢喜,自不必说,乃封百金赠封肃, 外谢甄家娘子许多物事,令其好生养赡,以待寻访女儿下落.封肃回家无话.

2 个答案:

答案 0 :(得分:4)

我们将STDOUT设置为:utf8 IO layer,因此说明不会显示数据格式错误,然后打开具有相同图层的文件,以便钻石不会读取格式错误的数据。 之后,在内部,而不是分裂空字符串,我们使用带有"East_Asian_Width: Wide" Unicode-like property的正则表达式。

utf8用于我个人的理智检查,可以删除(Y)。

use strict;
use warnings;
use 5.010;
use utf8;
use autodie;

binmode(STDOUT, ':utf8');

open my $fh, '<:utf8', 'sample_file.txt';

my ($total, $valid);
my %count;

while (<$fh>) {
    $total += length;
    for (/(\p{Ea=W})/g) {
        $valid++;
        $count{$_}++;
    }
}

say "Total things  = $total, valid words = $valid";
for my $word (sort keys %count) {
   say "$word \t was seen \t $count{$word} \t times.";
}
编辑:J-16 SDiZ和daxim指出sample_file.txt处于UTF-8的可能性很小。阅读他们的评论,然后看看perldoc中的Encode module,特别是“通过PerlIO编码”部分。

答案 1 :(得分:2)

我或许可以提供一些见解,但很难说我的回答是否“有用”。首先,我只会说英语,所以我显然不会说或读中文。我碰巧是RegexKitLite的作者,它是围绕ICU正则表达式引擎的Objective-C包装器。这显然不是perl,:)。

尽管如此,ICU正则表达式引擎恰好具有与您尝试做的非常相似的功能。具体来说,ICU正则表达式引擎包含UREGEX_UWORD修饰符选项,可以通过正常的(?w:...)语法动态打开该选项。此修饰符执行以下操作:

  

控制模式中\ b的行为。如果设置,则根据Unicode UAX 29,Text Boundaries中找到的单词的定义找到单词边界。默认情况下,通过将字符简单分类为“单词”或“非单词”来识别单词边界,这类似于传统的正则表达式行为。在空格和其他非单词字符的运行中,使用这两个选项获得的结果可能完全不同。

您可以在(?w:\b(.*?)\b)这样的正则表达式中使用它来“提取”字符串中的单词。在ICU正则表达式引擎中,它有一个相当强大的“破字引擎”,专门用于查找没有明确空间“字符”的书面语言中的单词分隔符,如英语。再一次,不读或写这些语言,我的理解是“itisroughlysomethinglikethis”。 ICU破坏引擎使用启发式算法,偶尔使用词典,以便能够找到单词分词。我的理解是,泰国恰好是一个特别困难的案例。事实上,我碰巧使用ฉันกินข้าว(泰语为“我吃饭”,或者我被告知)使用(?w)\b\s*的正则表达式对字符串执行split操作以提取这些话。如果没有(?w),您就无法分词。使用(?w),会生成ฉันกินข้าว字样。

如果上述“听起来像你遇到的问题”,那么这可能是原因。如果是这种情况,那么我不知道在perl中有任何方法可以实现这一点,但我不认为这个意见是权威的答案,因为我比{{1}使用ICU正则表达式引擎更频繁当我已经有一个:)时,显然没有正确的动机来找到一个有效的perl解决方案。希望这会有所帮助。