1

バイナリファイルから大量のバイトを読み取っています。RARファイルです。ヘッダーの仕様に次のように記載されているため、ファイルの 11 番目と 12 番目のバイトに興味があります。

HEAD_FLAGS ビットフラグ: 2 バイト

            0x0001  - Volume attribute (archive volume)
            0x0002  - Archive comment present
                      RAR 3.x uses the separate comment block
                      and does not set this flag.

            0x0004  - Archive lock attribute
            0x0008  - Solid attribute (solid archive)
            0x0010  - New volume naming scheme ('volname.partN.rar')
            0x0020  - Authenticity information present
                      RAR 3.x does not set this flag.

            0x0040  - Recovery record present
            0x0080  - Block headers are encrypted
            0x0100  - First volume (set only by RAR 3.0 and later)

            other bits in HEAD_FLAGS are reserved for
            internal use

私が遊んでいるファイルには000Dそれぞれ位置 11 と 12 があります。

これら 2 つの値はビット フラグであるため、意味がわかりません (理解できませんでした)。

これら 2 つの値は、byte長さが 12 バイトの配列に含まれています。このシーケンスで確認する必要があるのは、フラグ0x01000x0001が設定されているかどうかです。

私はこれで迷っています。ありがとう。


Hex エディターでいくつかのファイルを調べたところ、11 番目と 12 番目のバイトを一緒に読み取る必要があることがわかりました。そのため、仕様ではすべてのビット フラグが 4 文字の 16 進数コードであると記載されています。ビット フラグを個別にチェックすると、正しくない結果が得られます。


回答/ヒントからできるだけ多くの情報を吸収して、次の方法でこれを解決しました:

FileInputStream fisFileInputStream = new FileInputStream((new File("C:\\testarchive.r00"));

byte[] bytHeader = new byte[20]; //The first 20 bytes are the RAR header.
fisFileInputStream.read(bytHeader);

short val=(short)( ((bytHeader[10]&0xFF)<<8) | (bytHeader[11]&0xFF) ); //Joining the two bytes into a short

System.out.println("Volume Attribute (0x0001): " + ((val & 0x0001) != 0));
System.out.println("First volume (0x0100): " + ((val & 0x0100) != 0));

複数の RAR アーカイブでこのコードを試しました — スパンされたアーカイブ、スパンされていないアーカイブ、スパンされたアーカイブの最初のファイル、スパンされたアーカイブの別のファイル。

コード自体は、非常に小さな癖を除いて正常に動作します。16進値から反対の結果が得られます。つまり

スパン アーカイブ内の最初のファイルではないファイルを検査すると、ボリューム属性0x0001が設定されていない ( false) と表示され、「最初のボリューム」0x100が設定されている ( true) と表示されます。

スパン アーカイブの最初のファイルを検査すると、正反対の結果が得られます。

ここで、元の仕様が間違っている (可能性は非常に低い) と信じるようにコードを変更します。これは、これ0x0001がスパン アーカイブの最初のファイルであり、スパン アーカイブであることを0x0100意味します。

..しかし、ビットフラグロジックで何か間違っていると思います。何か案は?

4

3 に答える 3

3

ビットフラグの考え方は、フラグを足し合わせることで、多数のフラグを少数のバイトに詰め込むことができるということです。

この場合、そのリストに表示される各値が個別のバイトのペアであるとは言っていません。それらはすべて2バイトに詰め込まれています。ファイルを書き込んでいる場合は、これらの値を合計して、書き込む値を取得します。

たとえば、「アーカイブロック属性」、「新しいボリューム命名スキーム」、「暗号化されたブロックヘッダー」と言いたいとします。つまり、0x0004、0x0010、および0x0080を設定する必要があります。したがって、これらをすべて合計して、0x0004 + 0x0010 + 0x0080 = 0x0094とします。これが、私たちが書き込む値です。これが機能するためには、すべての値が1ビットであることが判明する必要があります。言い換えると、すべてが2の累乗である必要があります。

したがって、このドキュメントでは18バイトについては説明していません。2についてのみ説明します。

これを読み取るには、目的のフラグ値を使用してAND演算(&)を実行する必要があります。「暗号化されたブロックヘッダー」が設定されているかどうかを知りたい場合は、値を読み取り、0x0010とANDを付けてください。これは2バイトの値であるため、短くするか、正しいバイトを選択する必要があります。議論のために、私たちはそれを短くすることを言いましょう。それなら私たちは言うでしょう

if ((flags & 0x0010) != 0)

その式が真の場合、ビットが設定されます。falseの場合、ビットは設定されません。

ところで、それをバイトのストリームとして読み取る場合は、次のように書くことでそれらを短くすることができます。

short flags = b[0] | b[1]<<8;

(そこにキャストが必要かもしれません。忘れました。)

または、ファイルがlow-hiまたはhi-lowのどちらで書き込まれるかに応じて、b[0]とb[1]を切り替える必要がある場合があります。

于 2012-07-03T18:37:41.897 に答える
2
      FileInputStream fis = new FileInputStream("file.rar");
      byte[] ba = new byte[13];
      fis.read(ba);
      byte b11 = ba[11];
      byte b12 = ba[12];
      boolean flagIs0x10 = b12 == 0x10;
      System.out.println("flag is 0x10 = "+flagIs0x10);

両方のバイトが一緒に値0x10を持つ必要がある場合、つまり16ビットワードの場合、

      boolean flagIs0x10 = b11 == 0 && b12 == 0x10;

また

      boolean flagIs0x10 = b12 == 0 && b11 == 0x10;
于 2012-07-03T18:26:50.773 に答える
0

ここでは、算数を処理する簡単な方法を示します。次のユーティリティ関数は、Java BitSet を変更して、バイト値の内容に基づいてビット フラグを設定します。

static void setByte(BitSet bitSet, int byteNum, byte b) {
    int base = byteNum * 8;
    bitSet.set(base + 7, (b & 0x80) != 0);
    bitSet.set(base + 6, (b & 0x40) != 0);
    bitSet.set(base + 5, (b & 0x20) != 0);
    bitSet.set(base + 4, (b & 0x10) != 0);
    bitSet.set(base + 3, (b & 0x08) != 0);
    bitSet.set(base + 2, (b & 0x04) != 0);
    bitSet.set(base + 1, (b & 0x02) != 0);
    bitSet.set(base + 0, (b & 0x01) != 0);
}

複数のバイトをビットセットに入れたい場合に備えて、「byteNum」パラメーターを取ります。

バイト 0 はビットセット位置 0 ~ 7 になり、バイト 1 はビットセット位置 8 ~ 15 になります。

また、各バイト内では、上位ビットは位置 7、下位ビット位置は 0 です。

次に、2 バイトを取得したら、これを簡単に行うことができます。最初のバイトが上位ビット (0x0800 - 0x8000) で、2 番目のバイトが下位ビット (0x0001 - 0x0080) であると想定していますが、仕様でそれがわかるはずです。

byte buf[] = new byte[2];
inputStream.read(buf, 0, 2);

BitSet bitSet = new BitSet();
// low order byte is the second one
setByte(bitSet, 0, bytes[1]);
// high order byte is first
setByte(bitSet, 1, byte1[0]);

boolean archiveVolume = bitSet.get(0);
boolean commentPresent = bitSet.get(1);
...
boolean firstVolume = bitSet.get(8);
于 2012-07-03T20:08:21.990 に答える