Perl ワンライナーで join する(辞書引き結果を行データに追加するときに便利)
2013-12-23-1
[Programming]
よく使う Perl ワンライナーをメモ。
各行がセパレータで区切られたフィールドを持つ2つのファイルに対して、それぞれの指定したフィールドが同じ値を持つ行をくっつける処理を行うのがUNIX系の join コマンド。
しかし、join コマンドは、それぞれのファイルの対象フィールドがソートされていることが前提なので手間がかかる場面も。
そんなこともあり、Perl のワンライナーを使うことが多い。
そんなにシンプルではないけど、なんといっても「ソート済み一時ファイル」を作らなくて済むのが良い。
とりあえずテンプレート的な記述。
(推奨できない省略表記が含まれているので「その場しのぎ」以上の使い方には注意!)
BEGIN ブロックで最初のファイルを読み込んでハッシュに格納し、これを辞書として使う。すこし面倒な点は BEGIN 内でのファイル読み込みに "-a" の自動区切りが効かないこと。"-l" も効かないしなあ。
なお、perl なので単なる join 以上の処理ができるのも良い。例えば、テキストに辞書の単語のどれかが含まれているか否かをチェックする処理。
各行がセパレータで区切られたフィールドを持つ2つのファイルに対して、それぞれの指定したフィールドが同じ値を持つ行をくっつける処理を行うのがUNIX系の join コマンド。
% cat a.txt 12 学校 13 給食 20 六本木 58 恵比寿 % cat b.txt school SCHOOL G 12 六本木 ROPPONGI R 20 恵比寿 EBISU E 58 % join -1 1 -2 4 a.txt b.txt 12 学校 school SCHOOL G 20 六本木 六本木 ROPPONGI R 58 恵比寿 恵比寿 EBISU E
しかし、join コマンドは、それぞれのファイルの対象フィールドがソートされていることが前提なので手間がかかる場面も。
% join -1 2 -2 1 a.txt b.txt (うまくいかない) % sort -k 2 a.txt > a2.txt (漢字の部分をソート) % cat a2.txt 20 六本木 12 学校 58 恵比寿 13 給食 (b.txt の第1フィールドはすでにソートされているので処理不要) % join -1 2 -2 1 a2.txt b.txt 六本木 20 ROPPONGI R 20 恵比寿 58 EBISU E 58
そんなこともあり、Perl のワンライナーを使うことが多い。
そんなにシンプルではないけど、なんといっても「ソート済み一時ファイル」を作らなくて済むのが良い。
とりあえずテンプレート的な記述。
(推奨できない省略表記が含まれているので「その場しのぎ」以上の使い方には注意!)
% perl -anle 'BEGIN{open(h,shift);while(<h>){chomp;split; $d{$_[1]}=$_[0]}}print"$d{$F[0]}\t$_"if$d{$F[0]}' a.txt b.txt 20 六本木 ROPPONGI R 20 58 恵比寿 EBISU E 58
BEGIN ブロックで最初のファイルを読み込んでハッシュに格納し、これを辞書として使う。すこし面倒な点は BEGIN 内でのファイル読み込みに "-a" の自動区切りが効かないこと。"-l" も効かないしなあ。
なお、perl なので単なる join 以上の処理ができるのも良い。例えば、テキストに辞書の単語のどれかが含まれているか否かをチェックする処理。
% cat c.txt 六本木から恵比寿まで行った。 東京は良い天気です。 学校はどこかな。 給食を食べた。 コーヒーを飲んだ。 % cat a.txt 12 学校 13 給食 20 六本木 58 恵比寿 % perl -anle 'BEGIN{open(h,shift);while(<h>){chomp;split;$d{$_[1]}=$_[0]}; $p=join("|",keys%d)}if(/($p)/){print"HIT:$1($d{$1})\t$_"}' a.txt c.txt HIT:六本木(20) 六本木から恵比寿まで行った。 HIT:学校(12) 学校はどこかな。 HIT:給食(13) 給食を食べた。