私は2つの大きなファイル(27k行と450k行)です。それらは次のように見えます:
File1:
1 2 A 5
3 2 B 7
6 3 C 8
...
File2:
4 2 C 5
7 2 B 7
6 8 B 8
7 7 F 9
...
3番目の列が両方のファイルにある両方のファイルの行が必要です(AとFの行は除外されていることに注意してください)。
OUTPUT:
3 2 B 7
6 3 C 8
4 2 C 5
7 2 B 7
6 8 B 8
最善の方法は何ですか?
私は2つの大きなファイル(27k行と450k行)です。それらは次のように見えます:
File1:
1 2 A 5
3 2 B 7
6 3 C 8
...
File2:
4 2 C 5
7 2 B 7
6 8 B 8
7 7 F 9
...
3番目の列が両方のファイルにある両方のファイルの行が必要です(AとFの行は除外されていることに注意してください)。
OUTPUT:
3 2 B 7
6 3 C 8
4 2 C 5
7 2 B 7
6 8 B 8
最善の方法は何ですか?
grep、sed、cut を使用したオプションを次に示します。
列 3 を抽出します。
cut -d' ' -f3 file1 > f1c
cut -d' ' -f3 file2 > f2c
で一致する行を検索file1
:
grep -nFf f2c f1c | cut -d: -f1 | sed 's/$/p/' | sed -n -f - file1 > out
で一致する行を検索file2
:
grep -nFf f1c f2c | cut -d: -f1 | sed 's/$/p/' | sed -n -f - file2 >> out
出力:
3 2 B 7
6 3 C 8
4 2 C 5
7 2 B 7
6 8 B 8
非対称データ ファイルがあり、小さい方がメモリに収まる場合、このワンパス awk ソリューションは非常に効率的です。
parse.awk
FNR == NR {
a[$3] = $0
p[$3] = 1
next
}
a[$3]
p[$3] {
print a[$3]
delete p[$3]
}
次のように実行します。
awk -f parse.awk file1 file2
file1
2 つのうち小さい方はどこですか。
説明
FNR == NR
読み込まれます。file1
a[$3]
が のキーであるfile2
場合、行を出力します。$3
a
p[$3]
がキーであるfile1
場合に行を出力し、キーを削除します (一度だけ出力します)。$3
p
まず、3 番目のフィールドでファイルを並べ替えます。
sort -k 3 file1 > file1.sorted
sort -k 3 file2 > file2.sorted
次に、 comm を使用して 3 番目のフィールドで共通の値を取得します。
comm -12 <(cut -d " " -f 3 file1.sorted | uniq) <(cut -d " " -f 3 file2.sorted | uniq) > common_values.field
これで、ソートされた各ファイルを共通の値で結合できます。
join -1 3 -o '1.1,1.2,1.3,1.4' file1.sorted common_values.field > file.joined
join -1 3 -o '1.1,1.2,1.3,1.4' file2.sorted common_values.field >> file.joined
出力はフォーマットされているため、ファイルで使用されているものと同じフィールド順序が得られます。使用される標準 UNIX ツール: sort、comm、cut、uniq、join。は<( )
bash で動作します。他のシェルでは、代わりに一時ファイルを使用できます。
awk '{print $3}' file1 | sort | uniq > file1col3
awk '{print $3}' file2 | sort | uniq > file2col3
grep -Fx -f file1col3 file2col3 | awk '{print "\\w+ \\w+ " $1 " \\w+"}' > col3regexp
egrep -xh -f col3regexp file1 file2
2つのファイル内のすべての一意の列3を取得し、(を使用してgrep -F
)それらを交差させ、必要な列に一致する正規表現の束を出力しegrep
てから、2つのファイルからそれらを抽出するために使用します。
まず、3 番目の列から共通の値を取得します。次に、一致する 3 番目の列を持つ両方のファイルから行をフィルター処理します。
列が単一の文字で区切られている場合は、 を使用cut
して 1 つの列を抽出できます。任意の量の空白で区切ることができる列には、 を使用しますawk
。共通の列 3 の値を取得する 1 つの方法は、列を抽出し、並べ替えて、 を呼び出すことcomm
です。bash/ksh/zsh プロセス置換の使用:
comm -12 <(awk '{print $3}' file1 | sort -u) <(awk '{print $3}' file2 | sort -u)
これらをgrep
パターンに変換し、フィルタリングします。
comm -12 <(awk '{print $3}' file1 | sort -u) <(awk '{print $3}' file2 | sort -u) |
sed -e 's/[][.\|?*+^$]/\\&/g' \
-e 's/.*/^[^[:space]]+[[:space]]+[^[:space]]+[[:space]]+\1[[:space]]/' |
grep -E -f - file1 file2
上記の方法は、巨大なファイルでかなりうまく機能するはずです。しかし、500k 行では、巨大なファイルはありません。これらのファイルはメモリに問題なく収まる必要があり、単純な Perl ソリューションで問題ありません。両方のファイルをロードし、列の値を抽出して、一致する列を出力します。
perl -n -e '
@lines += 1;
$c = (split)[2];
$seen{$c}{$ARGV} = 1;
END {
foreach (@lines) {
$c = (split)[2];
print if %{$seen{$c}} == 2;
}
}' file1 file2