-
Notifications
You must be signed in to change notification settings - Fork 291
Regexp
Toshinori Sato (@overlast) edited this page Dec 7, 2016
·
4 revisions
辞書データを冗長にして異表記を吸収するのにも限界がある。
辞書データを生成する際には以下で述べる正規化処理を全て適用しているため、 解析対象のテキストに対して以下の正規化処理を適用すると、辞書中の語とマッチしやすくなる。
以下にmecab-ipadic-neologd のエントリを生成する際に、処理の各所に分散している正規化処理をまとめる。
生成時には色々置換と削除をしているが、最後に反映されているのは以下である。
- 0-9=> 0-9
- A-Z=> A-Z
- a-z=> a-z
半角の濁音と半濁音の記号が1文字扱いになってるので気をつけること。
以下はハイフンマイナスに置換する。
- MODIFIER LETTER MINUS SIGN(U+02D7)
- ARMENIAN HYPHEN(U+058A)
- ハイフン(U+2010)
- ノンブレーキングハイフン(U+2011)
- フィギュアダッシュ(U+2012)
- エヌダッシュ(U+2013)
- Hyphen bullet(U+2043)
- 上付きマイナス(U+207B)
- 下付きマイナス(U+208B)
- 負符号(U+2212)
以下は全角長音記号に置換する。
- エムダッシュ(U+2014)
- ホリゾンタルバー(U+2015)
- 横細罫線(U+2500)
- 横太罫線(横太罫線)
- SMALL HYPHEN-MINUS(U+FE63)
- 全角ハイフンマイナス(U+FF0D)
- 半角長音記号(U+FF70)
以下は削除する。
- 半角チルダ
- チルダ演算子
- INVERTED LAZY S
- 波ダッシュ
- WAVY DASH
- 全角チルダ
- /!”#$%&’()*+,−./:;<=>?@[¥]^_`{|}〜。、
- ・「」
- ' ' => ' '
- ' ' => ' '
- ' テキストの前' => 'テキストの前'
- 'テキストの後 ' => 'テキストの後'
- 検索 エンジン 自作 入門 を 買い ました !!! => 検索エンジン自作入門を買いました!!!
- Coding the Matrix => Coding the Matrix
- アルゴリズム C => アルゴリズムC
- Algorithm C => Algorithm C
上記の処理をそのまま素直に書くと以下のようになる。
実際はもう少し無駄が無いように工夫したり、必要のない処理については行わなければ良いだろう。
コード例では、Lingua::JA::Regular::Unicode という CPAN モジュールを「半角カタカナ => 全角カタカナ」の変換に使っている。
以下を参考にして、cpanm をインストールする。
http://perldoc.jp/docs/modules/App-cpanminus
その後、以下のコマンドで Lingua::JA::Regular::Unicode をインストールする。
cpanm Lingua::JA::Regular::Unicode
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use Encode;
use Lingua::JA::Regular::Unicode;
sub _normalize_str {
my ($str) = @_;
my $norm = "";
if ((defined $str) && ($str ne "")) {
$norm = Encode::decode_utf8($str);
$norm =~ tr/0-9A-Za-z/0-9A-Za-z/;
$norm = Lingua::JA::Regular::Unicode::katakana_h2z($norm);
my $hypon_reg = '(?:˗|֊|‐|‑|‒|–|⁃|⁻|₋|−)';
$norm =~ s|$hypon_reg|-|g;
my $choon_reg = '(?:﹣|-|ー|—|―|─|━)';
$norm =~ s|$choon_reg|ー|g;
my $chil_reg = '(?:~|∼|∾|〜|〰|~)';
$norm =~ s|$chil_reg||g;
$norm =~ tr/!"#$%&'()*+,-.\/:;<=>?@[\]^_`{|}~。、・「」/!”#$%&’()*+,-./:;<=>?@[¥]^_`{|}〜。、・「」/;
$norm =~ s| | |g;
$norm =~ s| {1,}| |g;
$norm =~ s|^[ ]+(.+?)$|$1|g;
$norm =~ s|^(.+?)[ ]+$|$1|g;
while ($norm =~ m|([\p{InCJKUnifiedIdeographs}\p{InHiragana}\p{InKatakana}\p{InHalfwidthAndFullwidthForms}\p{InCJKSymbolsAndPunctuation}]+)[ ]{1}([\p{InCJKUnifiedIdeographs}\p{InHiragana}\p{InKatakana}\p{InHalfwidthAndFullwidthForms}\p{InCJKSymbolsAndPunctuation}]+)|) {
$norm =~ s|([\p{InCJKUnifiedIdeographs}\p{InHiragana}\p{InKatakana}\p{InHalfwidthAndFullwidthForms}\p{InCJKSymbolsAndPunctuation}]+)[ ]{1}([\p{InCJKUnifiedIdeographs}\p{InHiragana}\p{InKatakana}\p{InHalfwidthAndFullwidthForms}\p{InCJKSymbolsAndPunctuation}]+)|$1$2|g;
}
while ($norm =~ m|([\p{InBasicLatin}]+)[ ]{1}([\p{InCJKUnifiedIdeographs}\p{InHiragana}\p{InKatakana}\p{InHalfwidthAndFullwidthForms}\p{InCJKSymbolsAndPunctuation}]+)|) {
$norm =~ s|([\p{InBasicLatin}]+)[ ]{1}([\p{InCJKUnifiedIdeographs}\p{InHiragana}\p{InKatakana}\p{InHalfwidthAndFullwidthForms}\p{InCJKSymbolsAndPunctuation}]+)|$1$2|g;
}
while ($norm =~ m|([\p{InCJKUnifiedIdeographs}\p{InHiragana}\p{InKatakana}\p{InHalfwidthAndFullwidthForms}\p{InCJKSymbolsAndPunctuation}]+)[ ]{1}([\p{InBasicLatin}]+)|) {
$norm =~ s|([\p{InCJKUnifiedIdeographs}\p{InHiragana}\p{InKatakana}\p{InHalfwidthAndFullwidthForms}\p{InCJKSymbolsAndPunctuation}]+)[ ]{1}([\p{InBasicLatin}]+)|$1$2|g;
}
$norm =~ tr/!”#$%&’()*+,-./:;<=>?@[¥]^_`{|}〜/!"#$%&'()*+,-.\/:;<=>?@[\]^_`{|}~/;
}
return $norm;
}
my $test_str = $ARGV[0];
print Encode::encode_utf8(_normalize_str($test_str))."\n";
$ ./test.pl " PRML 副 読 本 "
PRML副読本
$./test.pl "Coding the Matrix"
Coding the Matrix
$./test.pl "南アルプスの 天然水 Sparking Lemon レモン一絞り"
南アルプスの天然水Sparking Lemonレモン一絞り