sang.pl --- Suffix Array を用いて N-gram 統計をとるプログラム USAGE sang.pl -n NUM -t NUM FILENAME OPTION -n NUM : NUM で n-gram の n を指定する。 -t NUM : threshold: NUM以下の頻度のものは表示しない n-gram には改行は含まれない。 [実行例] % cat test ABCBACABBAACABCABCACABACABBACBACACAAABACCAB % makeary -q test ● arrayファイルの作成 % sang -n 6 -t 1 test ● 6-gram で頻度が 1 より大きいものを表示 2 ACABBA 2 BACABB % ./sang.pl -n 3 -t 4 test ● trigram で頻度が 4 より大きいものを表示 6 ACA 5 BAC 6 CAB
#!/usr/bin/perl
use strict;
use warnings;
use SUFARY;
use Encode;
use Getopt::Std;
use utf8;
use open ':utf8';
binmode STDOUT, ":utf8";
my %opts = ();
getopts("n:t:", \%opts);
my $ng = $opts{n} || 4;
my $threshold = $opts{t} || 3;
my $fn = shift @ARGV;
my $sa = SUFARY->new($fn);
my $n_ctr = 0;
my @ktoks = ();
for (my $i = 0; $i < $sa->{arraysize}; $i++) {
my $pos = $sa->get_position($i);
my ($from, $len) = $sa->get_line_info($pos);
my $s = $sa->get_string($pos, $from + $len - $pos);
$s =~ s/^([^\n]+)\n.*$/$1/;
my @ttoks = map {Encode::decode_utf8($_)}
($s =~ m{([\x00-\x7f]|[\xC0-\xDF][\x80-\xBF]|
[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3})}gsx);
if (cmp_tok(\@ktoks, \@ttoks) >= $ng) {
$n_ctr++;
} else {
output();
$n_ctr = 1;
}
@ktoks = @ttoks;
}
output() if $n_ctr >= 1;
sub output {
return if $n_ctr <= $threshold;
print "$n_ctr ".join("", @ktoks[0..($ng-1)])."\n";
}
sub cmp_tok {
my ($k_ref, $t_ref) = @_;
my $i;
for ($i = 0; $i < @$k_ref and $i < @$t_ref; $i++) {
last if $k_ref->[$i] ne $t_ref->[$i];
}
return $i;
}
N-gram カウント。% ./mkipu8.pl a.txt > a.txt.ary % mkary -so a.txt
% ./sang.pl -n 4 -t 3 a.txt 4 100円 5 「ぬんな 10 ぬんなり 4 ました。 4 まったり 5 んなり」