たつをの ChangeLog : 2013-12-23

よく使う Perl ワンライナーをメモ。

各行がセパレータで区切られたフィールドを持つ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)    給食を食べた。

たつをの ChangeLog
Powered by chalow