2

アーカイブされたバイナリ メッセージを含むファイルがあります。小さなファイルは約 600MB で、約 9000 件のメッセージが含まれています。各メッセージは、私が知っている特定の 4 バイト フラグで始まります。これは、メッセージ ヘッダーの最初の 4 バイトを示します (したがって、キャプチャする必要があります)。メッセージ ヘッダーは、すべてのメッセージで固定サイズです。メッセージ ヘッダーの後には、ヘッダーで識別されるサイズのペイロードが続きます。特定のメッセージ ヘッダーの先頭を見つけたら、ヘッダーの末尾までのバイト数がわかるので、それを使用してメッセージ内のバイト数を抽出し、このアーカイブ ファイルを解析して各メッセージを分離して処理する必要があります。 、4 バイト フラグの最初のバイトから指定されたメッセージ長の最後までのすべてのバイトが含まれていることを確認します。メッセージ間にはさまざまなパディングがあります。

ファイルのサイズが大きいため、ファイルを単一の配列として使用したくありません (すべての場合で可能ではありません)。したがって、私は と のようなものを見ていRandomAccessFileますFileInputStream。ファイルをスキャンして特定のバイト シーケンスを探し、そのシーケンスの最初のバイトから既知の長さまですべてのバイトを取得するのは簡単な作業ではないようです。RandomAccessFile、特にread(byte[])andseek()メソッドは、解決策を実装できるように思われます。

アイデアを与えるために、私の現在の実装にはfindFlag()、 の開始位置を取ると呼ばれるメソッドが含まれていRandomAccessFileます。その位置にシークし、そこから始まる 4 バイトを読み取ります。フラグが見つかった場合は、 を返しますstartPos。それ以外の場合は、再帰的に自分自身を呼び出し、移動してstartPos + 1、フラグが見つかるまで繰り返します。データ メッセージの一部として読み取った最後のバイトがわかっているので、そこからシークを開始します。

file.seek(startPos);

byte[] possibleFlag = new byte[4];

file.read(possibleFlag, 0, possibleFlag.length);

if (Arrays.equals(ByteUtils.intToBytes(Message.FLAG), possibleFlag)) {
    return startPos;
}
else {
    return findFlag(startPos + 1);
}

Java (Java 6 以前) または十分にテストされた外部ライブラリ (Apache ライブラリなど) で何かを見落としていますか? そうでない場合、Java でバイナリ データを処理するためのより良い解決策や、私の問題に特に適したアプローチはありますか?

4

3 に答える 3

2

java.nio.channels.FileChannel を使用してファイルをスキャンし、ファイルをメモリにマップするために使用する中間コピーを減らします。 代替のベンチマーク

于 2012-06-05T14:58:24.557 に答える
1

このアプローチ全体が無効に思えます。マジック バイトが他の場所に表示されないことをどのように確認できますか? たとえば、ペイロードまたはパディングで。これを考慮していただければ幸いです。

再帰を取り除きます。Java はテール コールの除去を行いません。反復バージョンは、より明確で高速になるはずです。

割り当て数を制限します。ファイル内の各バイトごとに 2 つの配列を割り当てることは、まったく受け入れられません。

を使用すれば、バッファ サイズと割り当てについて心配する必要はありませんFileChannelMappedByteBuffer.getInt(int)を使用してファイルを反復処理し、それを と比較できMessage.FLAGます。これは単純な for ループの 1 つに過ぎません。

于 2012-06-05T15:46:56.267 に答える
0

これは私には恐ろしく非効率に思えます。ファイルに対する最もコストのかかる操作は、内部ポインターを前後に移動するランダムな部分です。そして、あなたはそれをすべてのバイトに対して行います。+4、-3、+4、-3 など...パフォーマンス デス ワルツ。前への動きだけで完璧にできます。シーケンス全体ではなく、署名の最初のバイトだけを検索します。一致する場合は、次のバイトをテストします。失敗した場合は、最初のバイトの検索を再開してください。4 回連続で成功すると、サインを獲得したことになります。いつでも、あなたはただ前に進み続けます。シークは絶対に避けてください。

また、処理にかかる時間がまったく気にならない場合を除き、機能だけを理由に FileChannel を却下しないでください。参照されている統計は、100MB あたり MINUTES について話しており、その観察を裏付けることができます。FileChannel は、読み取りサイズが小さい RandomAccessFile よりも 2 桁高速です。最小のものが必要です :)

また、再帰は一般的に大胆不敵なプログラマーのしるしと考えられていますが、署名を含まない数百 MB のデータを VM に供給すると、この特定の使用法は VM の蓋を簡単に吹き飛ばす可能性があります。

于 2012-06-05T21:28:20.647 に答える