複数行のレコードを分割せずに大容量のテキストファイルを効率的に分割する方法は?

複数行のレコードを分割せずに大容量のテキストファイルを効率的に分割する方法は?

大きなテキストファイルがあります(gz以降約50Gb)。ファイルには4*N行またはNレコードが含まれます。つまり、各レコードは4行で構成されています。このファイルをそれぞれ入力ファイルサイズの約25%の4つの小さなファイルに分割したいと思います。レコード境界でファイルを分割する方法は?

簡単な方法は、zcat file | wc -l行数を取得し、その数を4で割ってからを使用することですsplit -l <number> file。ただし、この方法ではファイルを2回繰り返し、行数を非常に遅く計算します(36分)。もっと良い方法がありますか?

これ近いですが、私が望むものではありません。許容される回答は行計算も実行します。

編集する:

このファイルには fastq 形式のシーケンスデータが含まれています。 2つのレコードは次のとおりです(匿名)。

@NxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxGCGA+ATAGAGAG
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxTTTATGTTTTTAATTAATTCTGTTTCCTCAGATTGATGATGAAGTTxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
AAAAA#FFFFFFFFFFFFAFFFFF#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF<AFFFFFFFFFFAFFFFFFFFFFFFFFFFFFF<FFFFFFFFFAFFFAFFAFFAFFFFFFFFAFFFFFFAAFFF<FAFAFFFFA
@NxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxGCGA+ATAGAGAG
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxCCCTCTGCTGGAACTGACACGCAGACATTCAGCGGCTCCGCCGCCxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
AAAAA#FFFFF7FFFFFFAFFFFA#F7FFFFFFFFF7FFFFFAF<FFFFFFFFFFFFFFAFFF.F.FFFFF.FAFFF.FFFFFFFFFFFFFF.)F.FFA))FFF7)F7F<.FFFF.FFF7FF<.FFA<7FA.<.7FF.FFFAFF

すべてのレコードの最初の行はaで始まります。 (残念ながら、すべてのレコードの3行目になる可能性が@あります。以下のブレンダの説明を参照してください。)@

編集2:

zcat file > /dev/null31分かかります。

編集3: 最初の行だけで始まります@。他の誰もそうではありません。バラよりここ。記録を順番に保管する必要があります。生成されたファイルに何も追加すると機能しません。

ベストアンサー1

私はあなたがこれを行うことができないと思います。それは信頼できず、あなたが要求する方法ではありません。問題は、アーカイブの圧縮率が最初から最後まで均等に配布されない可能性があることです。圧縮アルゴリズムは、他の部分よりも一部に適しています。これがうまくいく方法です。したがって、圧縮ファイルのサイズによって分割を分割することはできません。

さらに、gzip元のサイズの4GBを超える圧縮ファイルの保存もサポートされていません。処理できません。したがって、信頼できるサイズを得るためにアーカイブを照会することはできません。なぜなら、これはユーザーをだますことができるからです。

4行 - 本当に簡単です。 4つのファイルの問題 - 圧縮されていないサイズを得るために、まずアーカイブを抽出せずに安定して均等に配布する方法がわかりません。頑張ったからできないと思います。

しかし、あなたは何をしますか?できる分割出力ファイルの最大サイズを設定し、これらのファイルが常に録音バリアから破壊されるようにします。簡単にできます。以下は、アーカイブを抽出し、特定のパラメータを持つ複数の明示的なパイプバッファを介してコンテンツをパイプし、gzip各ファイルを即座に解凍/再圧縮するように渡すことによって実行する小さなスクリプトです。また、各セグメントの最後の4行をstderrに印刷するためのいくつかのパイプトリックを追加しました。ddcount=$rptlz4tee

(       IFS= n= c=$(((m=(k=1024)*k)/354))
        b=bs=354xk bs=bs=64k
        pigz -d </tmp/gz | dd i$bs o$b |
        while   read -r line _$((n+=1))
        do      printf \\n/tmp/lz4.$n\\n
        { {     printf %s\\n "$line"
                dd count=$c i$b o$bs
        }|      tee /dev/fd/3|lz4 -BD -9 >/tmp/lz4.$n
        } 3>&1| tail -n4 |tee /dev/fd/2 |
                wc -c;ls -lh /tmp/[gl]z*
        done
)

これはすべての入力が処理されるまで続きます。特定の割合(取得できません)に分割するのではなく、分割ごとの最大生バイト数に分割します。とにかく問題の最大の部分は、アーカイブが大きすぎるため、信頼できるサイズを取得できないことです。何をしても二度としないでください。今回のラウンドでは、分割を4GBの部分より小さくしてください。この小さなスクリプトを使用すると、少なくとも圧縮されていないバイトをディスクに書き込むことなくこれを行うことができます。

これは基本のみを維持する簡単なバージョンです。すべての報告内容を追加するわけではありません。

(       IFS= n= c=$((1024*1024/354))
        pigz -d | dd ibs=64k obs=354xk |
        while   read -r line _$((n+=1))
        do {    printf %s\\n "$line"
                dd count=$c obs=64k ibs=354xk
        }  |    lz4 -BD -9  >/tmp/lz4.$n
        done
)  </tmp/gz

最初と同じことを行い、ほとんどの場合、言うことはあまりありません。また、混乱した部分が少なくなり、何が起こっているのかをより簡単に確認できます。

問題は、繰り返しごとに1行を処理するIFS=ことです。入力が終わったらループを終了する必要があるため、これを選択しましたreadreadあなたの記録によって異なります -サイズ- あなたの例によると、それぞれ354バイトです。gzipテストするために、任意のデータを含む4GB以上のアーカイブを作成しました。

次のようにランダムなデータを取得します。

(       mkfifo /tmp/q; q="$(echo '[1+dPd126!<c]sc33lcx'|dc)"
        (tr '\0-\33\177-\377' "$q$q"|fold -b144 >/tmp/q)&
        tr '\0-\377' '[A*60][C*60][G*60][N*16][T*]' | fold -b144 |
        sed 'h;s/^\(.\{50\}\)\(.\{8\}\)/@N\1+\2\n/;P;s/.*/+/;H;x'|
        paste "-d\n" - - - /tmp/q| dd bs=4k count=kx2k  | gzip
)       </dev/urandom >/tmp/gz 2>/dev/null

...しかし、すでにデータとすべてがあるので、あまり心配する必要はありません。ソリューションに戻って...

デフォルトでは、pigz解凍速度は実際より少し速いようですzcat。圧縮されていない出力ストリームをパイプし、出力をdd354バイトのサイズの倍数の書き込みブロックにバッファリングします。ループはread$line反復で入力がまだ到着しているかどうかを一度テストし、その期間中にバッファリングプロセスと同期するために、特に354バイトの倍数サイズのブロックを読み取るために別のループを呼び出すprintf前に一度ループします。初期化の理由により、繰り返しごとに短い読み取りが発生しますが、コレクタープロセスでとにかく印刷されるため、重要ではありません。printflz4ddddread $linelz4

繰り返しごとに約1GBの圧縮されていないデータを読み取り、インストリームデータを約650Mb程度に圧縮するように設定しました。lz4ほとんどすべての有用な圧縮方法よりはるかに高速です。これが私が待つのが好きではないので、ここでこの方法を選択した理由です。xzしかし、実際の圧縮の面ではより良いかもしれません。しかし、一つは、lz4RAM速度に近い速度で解凍できることが多いということです。これはlz4、とにかくアーカイブをメモリに書き込むことができるかのように、アーカイブをすばやく解凍できることを意味します。

大規模な反復ごとにいくつかのレポートが作成されます。両方のループは、送信された生のddバイト数、速度などのレポートを印刷します。大きなループはまた、各サイクルで同じバイト数で最後の4行の入力を印刷し、lsアーカイブを作成したディレクトリを印刷しますlz4。以下はいくつかのラウンドの結果です。

/tmp/lz4.1
2961+1 records in
16383+1 records out
1073713090 bytes (1.1 GB) copied, 169.838 s, 6.3 MB/s
@NTACGTANTTCATTGGNATGACGCGCGTTTATGNGAGGGCGTCCGGAANGC+TCTCTNCC
TACGTANTTCATTGGNATGACGCGCGTTTATGNGAGGGCGTCCGGAANGCTCTCTNCCGAGCTCAGTATGTTNNAAGTCCTGANGNGTNGCGCCTACCCGACCACAACCTCTACTCGGTTCCGCATGCATGCAACACATCGTCA
+
I`AgZgW*,`Gw=KKOU:W5dE1m=-"9W@[AG8;<P7P6,qxE!7P4##,Q@c7<nLmK_u+IL4Kz.Rl*+w^A5xHK?m_JBBhqaLK_,o;p,;QeEjb|">Spg`MO6M'wod?z9m.yLgj4kvR~+0:.X#(Bf
354

-rw-r--r-- 1 mikeserv mikeserv 4.7G Jun 16 08:58 /tmp/gz
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:32 /tmp/lz4.1

/tmp/lz4.2
2961+1 records in
16383+1 records out
1073713090 bytes (1.1 GB) copied, 169.38 s, 6.3 MB/s
@NTTGTTGCCCTAACCANTCCTTGGGAACGCAATGGTGTGANCTGCCGGGAC+CTTTTGCT
TTGTTGCCCTAACCANTCCTTGGGAACGCAATGGTGTGANCTGCCGGGACCTTTTGCTGCCCTGGTACTTTTGTCTGACTGGGGGTGCCACTTGCAGNAGTAAAAGCNAGCTGGTTCAACNAATAAGGACNANTTNCACTGAAC
+
>G-{N~Q5Z5QwV??I^~?rT+S0$7Pw2y9MV^BBTBK%HK87(fz)HU/0^%JGk<<1--7+r3e%X6{c#w@aA6Q^DrdVI0^8+m92vc>RKgnUnMDcU:j!x6u^g<Go?p(HKG@$4"T8BWZ<z.Xi
354

-rw-r--r-- 1 mikeserv mikeserv 4.7G Jun 16 08:58 /tmp/gz
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:32 /tmp/lz4.1
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:35 /tmp/lz4.2

おすすめ記事