Disons que j'essaye de lire à partir d'un InputStream Java comme ceci:

ZipInputStream zis = new ZipInputStream(new FileInputStream("C:\\temp\\sample3.zip"));
zis.getNextEntry();
byte[] buffer2 = new byte[2];
int count = zis.read(buffer2));
if(count != -1) //process...
else...//something wrong, abort

J'analyse un fichier binaire et je mets mon tampon à 2 dans ce cas parce que je veux lire le prochain court. Je définirais mon tampon à la taille 4 si je veux lire le prochain int et ainsi de suite pour les autres types. Le problème est que parfois zis.read (tampon) ne remplit pas le tampon même si je sais qu'il y a suffisamment de données non lues pour remplir le tampon. Je pourrais simplement vider tout le contenu du fichier dans un tableau et analyser cela, mais je finis par implémenter mon propre lecteur de flux pour faire ce qui semble être une réinvention de la roue. Je pourrais également implémenter une fonction read () qui vérifie le nombre de lectures et s'il est inférieur à buffersize, demander plus de données pour remplir le tampon, mais c'est inefficace et laid. Y a-t-il une meilleure manière de faire cela?

Il s'agit d'une question complémentaire à une question publiée ici:

Erreurs d'extraction Java ZipInputStream

1
PentiumPro200 20 avril 2017 à 16:52

3 réponses

Meilleure réponse

Y a-t-il une meilleure manière de faire cela?

Eh bien ... un ZipInputStream hérite finalement de InputStream donc vous devriez pouvoir l'envelopper avec un BufferedInputStream puis un DataInputStream et lire les données en utilisant readShort , readInt et ainsi de suite.

Quelque chose comme ça:

while (zis.getNextEntry() != null) {
  DataInputStream dis = new DataInputStream(new BufferedInputStream(zis));
  boolean done = false;
  do {
    short s = dis.readShort();
    int i = dis.readInt();
    ...
  } while (!done);
}

NB: vous ne devez pas fermer le flux dis car cela entraînerait la fermeture du zis. (Évidemment, le zis doit être fermé à un niveau externe pour éviter une fuite de ressources.)

Le BufferedInputStream dans la pile garantit que vous ne faites pas beaucoup de petites lectures sur le flux sous-jacent ... ce qui serait mauvais.

Le seul problème possible est que ses méthodes ont des idées particulières sur la façon dont les données binaires sont représentées; par exemple. les nombres sont bigendiens. Si c'est un problème, envisagez de lire l'intégralité de l'entrée zip dans un tableau d'octets et de l'encapsuler dans un ByteBuffer.

1
Stephen C 20 avril 2017 à 15:01

ZipInputStream est conforme au contrat défini par InputStream. Les méthodes read (byte [] ...) sont autorisées et documentées pour renvoyer soit -1 pour la fin du flux, soit toute valeur entre (1 ... longueur demandée).

Et il y a une bonne raison pour laquelle l'API est définie de cette façon, elle donne à l'implémentation la liberté de renvoyer des données partielles dès qu'elles sont disponibles sans blocage pendant de longues périodes en attendant que les données deviennent disponibles (pensez à SocketInputStream).

Si vous avez besoin d'une quantité minimale de données, vous devez appeler read à plusieurs reprises jusqu'à ce que vous ayez lu autant de données que nécessaire pour continuer le traitement.

En ce qui concerne "c'est inefficace et laid", la lecture de petites quantités de données via les méthodes de lecture en bloc entraîne sa propre surcharge, et peut-être dans le code que vous montrez également la création d'un octet d'ordures [] pour chaque entité de données que vous lisez. Pour lire une poignée d'octets, vous pouvez simplement utiliser la méthode read () qui renvoie un seul octet, implémentée dans une méthode utilitaire simple, par exemple:

 static short readShort(InputStream in) throws IOException {
      short s = 0;
      for (int i=0; i<2; ++i) {
          int read = in.read();
          if (read < 0)
              throw new IOException("unexpected end of stream");
          s = (short) ((s << 8) | read);
      }
      return s;
 }

(cela peut être facilement adapté à d'autres types primitifs)

Les E / S sur un octet sont dans la plupart des cas totalement acceptables, à condition de veiller à ce que InputStream soit enveloppé dans un BufferedInputStream. La surcharge moyenne se réduit alors à quelques vérifications de limites d'index de tableau dans BufferedInputStream. Cela ne provoquera pas un nombre excessif d'appels vers la source de données native.

0
Durandal 20 avril 2017 à 14:32

Vous devez vérifier le nombre d'octets et continuer à lire jusqu'à ce que vous ayez toutes les informations dont vous avez besoin

zis.getNextEntry();
byte[] buffer2 = new byte[2];
int count = 0;
while (count < 2) {
  int bytesRead = zis.read(buffer2, count, 2 - count));
  if(bytesRead != -1) {
    count += bytesRead;
  }
  else...//something wrong, abort
}
//process...
0
ControlAltDel 20 avril 2017 à 14:15