52

Java でレガシー形式のバイナリ ファイルを読み取る必要があります。

簡単に言えば、ファイルには、いくつかの整数、バイト、および固定長の文字配列で構成されるヘッダーがあり、その後に整数と文字で構成されるレコードのリストが続きます。

他の言語では、ヘッダーとレコードのバイト単位の表現であるstructs (C/C++) またはs (Pascal/Delphi) を作成します。record次にsizeof(header)、バイトをヘッダー変数に読み込み、レコードに対して同じことを行います。

このようなもの: (Delphi)

type
  THeader = record
    Version: Integer;
    Type: Byte;
    BeginOfData: Integer;
    ID: array[0..15] of Char;
  end;

...

procedure ReadData(S: TStream);
var
  Header: THeader;
begin
  S.ReadBuffer(Header, SizeOf(THeader));
  ...
end;

Javaで似たようなことをする最善の方法は何ですか? すべての値を独自に読み取る必要がありますか、またはこの種の「ブロック読み取り」を行う他の方法はありますか?

4

12 に答える 12

36

私の知る限り、Java ではファイルを読み取りをブロックするのではなく、バイト単位で読み取るように強制されます。Java オブジェクトをシリアライズする場合は、話が異なります。

示されている他の例では、File でDataInputStreamクラスを使用していますが、ショートカットを使用することもできます: RandomAccessFileクラス:

RandomAccessFile in = new RandomAccessFile("filename", "r");
int version = in.readInt();
byte type = in.readByte();
int beginOfData = in.readInt();
byte[] tempId;
in.read(tempId, 0, 16);
String id = new String(tempId);

より簡単になる場合は、応答オブジェクトをクラスに変換できることに注意してください。

于 2008-11-10T14:41:08.197 に答える
20

Preonを使用する場合は、次のことを行うだけです。

public class Header {
    @BoundNumber int version;
    @BoundNumber byte type;
    @BoundNumber int beginOfData;
    @BoundString(size="15") String id;
}

これができたら、次の 1 行を使用して Codec を作成します。

Codec<Header> codec = Codecs.create(Header.class);

そして、次のようにコーデックを使用します。

Header header = Codecs.decode(codec, file);
于 2009-08-12T14:52:04.033 に答える
19

次のように DataInputStream クラスを使用できます。

DataInputStream in = new DataInputStream(new BufferedInputStream(
                         new FileInputStream("filename")));
int x = in.readInt();
double y = in.readDouble();

etc.

これらの値を取得したら、好きなように処理できます。詳細については、API で java.io.DataInputStream クラスを参照してください。

于 2008-11-10T14:31:32.380 に答える
10

私はあなたを誤解しているかもしれませんが、ハードディスクから読みたいものをバイトごとに正確に表現し、すべてのものをメモリにコピーし、そこから操作?

もしそうなら、あなたは非常に危険なゲームをプレイしています. 少なくとも C では、標準は構造体のメンバーのパディングやアラインメントなどを強制しません。大きい/小さいエンディアンやパリティ ビットなどは言うまでもありません...したがって、コードがたまたま実行されたとしても、移植性が低く危険です。コンパイラの作成者が将来のバージョンで考えを変えないことに依存します。

オートマトンを作成して、HD から読み取られる構造 (バイトごとのバイト) が有効であることを検証し、実際に問題がない場合はメモリ内構造を埋めることをお勧めします。プラットフォームとコンパイラの独立性は得られますが、数ミリ秒を失う可能性があります (最新の OS が多くのディスク読み取りキャッシュを行うように見えるほどではありません)。さらに、コードを別の言語に簡単に移植できます。

投稿編集: ある意味で私はあなたに同情します. DOS/Win3.11 の古き良き時代に、BMP ファイルを読み取る C プログラムを作成したことがあります。そして、まったく同じテクニックを使用しました。Windows用にコンパイルしようとするまでは、すべてがうまくいきました-おっと!! Int の長さは 16 ビットではなく 32 ビットになりました。Linux でコンパイルしようとしたとき、gcc には Microsoft C (6.0!) とはビット フィールド割り当てのルールが大きく異なることがわかりました。移植可能にするために、マクロのトリックに頼らなければなりませんでした...

于 2008-11-10T15:58:16.277 に答える
4

これは、ByteBuffer (Java NIO) を使用してバイトを読み取るためのリンクです。

http://exampledepot.com/egs/java.nio/ReadChannel.html

于 2008-11-10T16:10:24.273 に答える
4

FileInputStream を使用すると、バイト単位で読み取ることができると思います。そのため、FileInputStream でファイルを開き、sizeof(header) を読み込みます。ヘッダーの形式とサイズは固定されていると想定しています。最初の投稿では言及されていませんが、ヘッダーにオプションの引数と異なるサイズがある場合、はるかに複雑になるため、そうであると仮定します。

情報を取得したら、既に読み取ったバッファーの内容を割り当てるヘッダー クラスが存在する可能性があります。次に、同様の方法でレコードを解析します。

于 2008-11-10T14:18:20.630 に答える
3

データのByteBuffer表現をラップするオブジェクトを作成し、バッファから直接読み取るゲッターを提供します。このようにして、データをバッファーからプリミティブ型にコピーすることを回避します。さらに、MappedByteBufferを使用してバイト バッファーを取得できます。バイナリ データが複雑な場合は、クラスを使用してモデル化し、各クラスにバッファのスライス バージョンを与えることができます。

class SomeHeader {
    private final ByteBuffer buf;
    SomeHeader( ByteBuffer fileBuffer){
       // you may need to set limits accordingly before
       // fileBuffer.limit(...)
       this.buf = fileBuffer.slice();
       // you may need to skip the sliced region
       // fileBuffer.position(endPos)
    }
    public short getVersion(){
        return buf.getShort(POSITION_OF_VERSION_IN_BUFFER);
    }
}

また、バイト バッファーから符号なし値を読み取るためのメソッドも役立ちます。

HTH

于 2010-03-04T11:52:42.870 に答える
3

他の人が言及しているように、DataInputStream と Buffers はおそらく Java でバイナリ データを処理するための低レベル API です。

ただし、おそらくConstruct (wiki ページにも良い例があります: http://en.wikipedia.org/wiki/Construct_(python_library)のようなものが必要ですが、Java.

私は (Java バージョンの) オフハンドを知りませんが、そのアプローチ (コードで構造体を宣言的に指定する) を採用することは、おそらく正しい方法です。Javaの適切な流暢なインターフェースを使用すると、おそらく DSL に非常に似たものになるでしょう。

編集:少しグーグルすると、これが明らかになります:

http://javolution.org/api/javolution/io/Struct.html

あなたが探しているのはそのようなものかもしれません。それが機能するかどうかはわかりませんが、始めるには賢明な場所のようです.

于 2008-11-10T16:15:28.037 に答える
2

私はこの種のことを Java で行うためのテクニックを書き上げました - ビットフィールドを読み取る古い C ライクなイディオムに似ています。これは単なる開始点ですが、拡張できることに注意してください。

ここ

于 2009-05-05T01:06:43.070 に答える
1

少し前に、リフレクションと解析を使用したバイナリ データの読み取りに関するこの記事を見つけました。この場合、作成者はリフレクションを使用して Java バイナリ .class ファイルを読み取ります。ただし、データをクラス ファイルに読み込む場合は、役立つ場合があります。

于 2008-11-10T15:53:04.120 に答える
1

以前は、DataInputStream を使用して、指定された順序で任意の型のデータを読み取っていました。これでは、ビッグ エンディアン/リトルエンディアンの問題を簡単に説明することはできません。

1.4 の時点では、java.nio.Buffer ファミリが適している可能性がありますが、コードは実際にはもっと複雑になるようです。これらのクラスは、エンディアンの問題の処理をサポートしています。

于 2008-11-10T14:32:33.550 に答える