6

私は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

最善の方法は何ですか?

4

4 に答える 4

3

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

file12 つのうち小さい方はどこですか。

説明

  • ブロックは2 つのハッシュにFNR == NR読み込まれます。file1
  • a[$3]が のキーであるfile2場合、行を出力します。$3a
  • p[$3]がキーであるfile1場合に行を出力し、キーを削除します (一度だけ出力します)。$3p
于 2012-09-16T00:35:36.207 に答える
3

まず、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 で動作します。他のシェルでは、代わりに一時ファイルを使用できます。

于 2012-09-15T23:56:20.700 に答える
2
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つのファイルからそれらを抽出するために使用します。

于 2012-09-15T23:17:44.100 に答える
1

まず、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
于 2012-09-15T23:28:04.520 に答える