53 votes

Meilleur moyen de lire des fichiers binaires structurés avec Java

Je dois lire un fichier binaire dans un format hérité avec Java.

En un mot, le fichier contient un en-tête composé de plusieurs entiers, octets et de longueur fixe des tableaux de char, suivi par une liste de documents qui sont également composées de nombres entiers et de caractères.

Dans toute autre langue, je voudrais créer structs (C/C++) ou records (Pascal/Delphi) qui sont octet-par-octet représentations de l'en-tête et le dossier. Puis j'avais lu sizeof(header) d'octets dans un en-tête variable et faire de même pour les enregistrements.

Quelque chose comme ceci: (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;

Quelle est la meilleure façon de faire quelque chose de similaire avec Java? Dois-je lire à chaque valeur sur son propre ou est-il un autre moyen de faire ce genre de "bloc de lire"?

36voto

Powerlord Points 43989

À ma connaissance, Java vous oblige à lire un fichier en octets plutôt que d'être en mesure de bloquer les lire. Si vous avez été la sérialisation des objets Java, ce serait une autre histoire.

Les autres exemples présentés utiliser le DataInputStream classe avec un Fichier, mais vous pouvez également utiliser un raccourci: Le RandomAccessFile classe:

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);

Notez que vous pouvez tourner la responce des objets dans une classe, pour le rendre plus facile.

20voto

Wilfred Springer Points 5430

Si vous utilisiez Preon , tout ce que vous auriez à faire est le suivant:

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

Une fois que vous avez cela, vous créez un codec en utilisant une seule ligne:

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

Et vous utilisez le codec comme ceci:

 Header header = Codecs.decode(codec, file);
 

19voto

Vincent Ramdhanie Points 46265

Vous pouvez utiliser la classe DataInputStream comme suit:

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

etc.
 

Une fois que vous obtenez ces valeurs, vous pouvez les utiliser comme bon vous semble. Recherchez la classe java.io.DataInputStream dans l'API pour plus d'informations.

10voto

Joe Pineda Points 2130

J'ai peut-être mal compris, mais il me semble que vous êtes en train de créer des structures en mémoire que vous espérez être un octet par octet représentation précise de ce que vous voulez lire à partir d'un disque dur, puis copiez l'ensemble des trucs sur la mémoire et manipuler de là?

Si c'est effectivement le cas, vous jouez à un jeu très dangereux. Au moins en C, la norme n'appliquent pas les choses comme rembourrage ou de l'alignement des membres d'une structure. Pour ne pas mentionner des choses comme la taille de l'endianness ou des bits de parité... Donc même si votre code arrive à terme, il est très non-portable et risqué - vous dépend du compilateur, créateur de ne pas changer son esprit sur les futures versions.

Mieux vaut créer un automate à valider à la fois la structure en cours de lecture (octet par octet) de la HD est valide, et le remplissage d'une structure en mémoire si c'est bien OK. Vous pouvez perdre quelques millisecondes (pas tellement que cela puisse paraître pour de nouveaux Systèmes d'exploitation fais beaucoup de lecture du disque en cache) si vous gagnez la plate-forme de compilateur et de l'indépendance. De Plus, votre code sera facilement porté à une autre langue.

Post Edit: je compatis avec vous. Dans les bons vieux jours de DOS/Win3.11, une fois, j'ai créé un programme en C pour lire des fichiers au format BMP. Et a utilisé exactement la même technique. Tout était beau jusqu'à ce que j'ai essayé de le compiler pour Windows - oups!! Int a maintenant une longueur de 32 bits, plutôt que de 16! Quand j'ai essayé de compiler sur Linux, découvert gcc avait très différentes règles pour les champs de bits d'allocation de Microsoft C (6.0!). J'ai dû recourir à la macro astuces pour le rendre portable...

8voto

Ko-Chih Wu Points 185

J'ai utilisé Javolution et javastruct, deux poignées, la conversion entre les octets et les objets.

Javolution fournit des classes qui représentent des types C. Tout ce que vous devez faire est d'écrire une classe qui décrit la structure C. Par exemple, à partir de la C fichier d'en-tête,

struct Date {
    unsigned short year;
    unsigned byte month;
    unsigned byte day;
};

devrait être traduit en:

public static class Date extends Struct {
    public final Unsigned16 year = new Unsigned16();
    public final Unsigned8 month = new Unsigned8();
    public final Unsigned8 day   = new Unsigned8();
}

Puis appelez setByteBuffer afin d'initialiser l'objet:

Date date = new Date();
date.setByteBuffer(ByteBuffer.wrap(bytes), 0);

javastruct utilise les annotations pour définir des champs dans la structure.

@StructClass
public class Foo{

    @StructField(order = 0)
    public byte b;

    @StructField(order = 1)
    public int i;
}

Pour initialiser un objet:

Foo f2 = new Foo();
JavaStruct.unpack(f2, b);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X