comment puis-je pour manipuler les bits.
je pouvoir lir un fichier lire les caractères et mettre le bits (correspondants) dans un tableau!
Version imprimable
comment puis-je pour manipuler les bits.
je pouvoir lir un fichier lire les caractères et mettre le bits (correspondants) dans un tableau!
va falloir être plus précis, tu parles de caractères or un caractère peut être codé sur un nombre différent de bit (selon l'encodage). Ensuite tu veux mettre des bits dans un tableau, sous quelle forme ?
Salut
On peut faire ca avec un ByteArrayInputStream
Voili voilou :
Code:
1
2
3
4
5
6
7
8
9 public byte[] getBytes(String filepath) throws IOException { FileInputStream in = new FileInputStream(new File(filepath)); byte[] b = new byte[in.available()]; in.read(b); in.close(); return b; }
Salut,
Un OutOfMemoryError... mais c'est tout à fait normal ;)
Mais il y a surtout trois points qui me chiffonne bien plus que cela :
- La fermeture du flux n'est pas dans un bloc finally... en cas d'erreur lors de la lecture le fichier ne sera pas fermé...
- available() ne renvoi pas la taille du fichier, mais une "estimation" du nombre de byte qui peuvent être lu sans provoquer de bloquage d'I/O. Avec de petit fichier cela peut très bien marcher... mais si le fichier est de taille conséquente on risque de tronquer la lecture ! De plus certain type d'InputStream peuvent très bien toujours renvoyer 0 !!!
- De même, rien ne garantie qu'un seul appel à read() permettra de lire la totalité du fichier...
Plutôt un ByteArrayOutputStream...
Cela donnerait ceci, en utilisant une lecture par bloc de 8192 bytes :
Le ByteArrayOutputStream se redimensionne automatiquement lorsque c'est nécessaire (tout comme les StringBuffer/StringBuilder). Le gros problème de cette solution c'est que la méthode toByteArray() renvoi une copie du tableau généré, et que pendant un moment on se retrouve avec les données en double en mémoire, ce qui peut être pénalisant pour de gros fichier.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 public byte[] getBytes(String filepath) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); FileInputStream in = new FileInputStream(filepath); try { // On lit par bloc de 8192 bytes : byte[] buf = new byte[8192]; int len; while ( (len=in.read(buf)) >= 0) { baos.write(buf, 0, len); } } finally { in.close(); } return baos.toByteArray(); }
:arrow: Dans ce cas il faudrait plutôt se baser sur la méthode length() de File et gérer la lecture en boucle :
A noter qu'on pourrait également utiliser un ByteBuffer... voir directement un MappedByteBuffer pour de gros fichier (voir FileChannel.map()).Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public byte[] getBytes(String filepath) throws IOException { File file = new File(filepath); long length = file.length(); if (length<=0) { throw new IOException("Taille de fichier incorrecte..."); /* Note : dans ce cas on pourrait envisager une lecture * via un ByteArrayOutputStream, car length() peut renvoyer 0 * avec certains fichiers spéciaux du système d'exploitation */ } if (length>Integer.MAX_VALUE) { throw new IOException("Fichier trop grand..."); /* Note : cela permet d'éviter des erreurs à cause * du cast de long en int qui donnerait une valeur négatives */ } // Création du buffer : int toRead = (int) length; byte[] buf = new byte[toRead]; FileInputStream in = new FileInputStream(filepath); try { int index = 0; int read; while (toRead>0) { read = in.read(buf, index, toRead); index += read; toRead -= toRead; } } finally { in.close(); } return buf; }
a++
En temps normal je mets toujours le close() dans un finally mais là c'est vrai j'avais la flemme de le mettre pour l'exemple.
Je ne savais pas que available() retourne une estimation, c'est bon à savoir, merci !
Sur des petits fichiers je n'ai jamais eut aucun problème, quant aux gros fichier je ne renvoie pas un tableau de byte[] mais je le traite par packets puisque tu vas l'avoir tout pareil ton OutOfMemoryError si tu charges un fichier de 4Go dans un byte[], non ?
Oui mais j'aime bien le répéter car il me semble qu'il est un peu trop souvent oublié ;)
C'est bien le problème : la plupart du temps cela marche correctement !
Mais cela ne dépend pas que de la taille du fichier, mais également de la JVM ou du système d'exploitation, voir même du type de matériel (lecteur de disquette) ou de l'emplacement du fichier (lecteur réseaux).
:arrow: Le jour où cela ne fonctionne plus le problème est difficile à trouver :?
Pour le OutOfMemory c'est un autre problème par contre : il est clair que si on doit traiter de très gros fichier il faut éviter de les charger en entier en mémoire ;)
a++
Merci pour ces précisions adiGuba. Effectivement je connais quelqu'un qui a eut un problème de lecture de fichier et il me disait que cela marchait sur certaines JVM et pas sur d'autres.
Pour l'avoir déjà utilisé, je n'y ai pas trouvé d'avantages dans la manipulation de fichiers texte (encodage/decodage en mémoire quand même), ce que cherche à faire l'auteur du post.
J'ai peut-être loupé un épisode sur le MappedByteBuffer, mais si qqn a une solution toute faite (dans le JDK) pour gérer une sorte de "MappedStringBuffer" couplé à un jeu de caractères particulier et paramétrable, qu'il se fasse connaître car je suis fortement intéressé.
;)
C'est justement le danger de ces méthodes dont le comportement n'est pas assuré ou variable ;)
C'est une solution... mais elle pose aussi un problème (sinon ce serait trop simple).
Comme la plupart des classes qui gère un tableau redimensionnable, le tableau est généralement bien plus grand que la taille réellement utile. Ainsi à chaque modification du tableau si il est trop petit sa taille est doublé. Cela évites des redimensionnement trop fréquent qui peuvent plomber les perfs (il me semble que Firefox avait eu un bug similaire avec la lecture de son fichier historique).
Par contre cela consomme logiquement plus de mémoire (à moins d'avoir beaucoup de chance pour tomber pile poil).
A la rigueur cela pourrait être une solution si on utilise le constructeur de ByteArrayOutputStream en lui spécifiant une taille initiale et que l'on est sûr de ne pas la dépasser...
Sinon il n'y a pas vraiment d'autre solution. Malheureusement les tableaux ne sont pas directement resizable en Java... :(
Et en utilisant la méthode Charset.decode() ?
Code:
1
2
3 MappedByteBuffer byteBuffer = channel.map(MapMode.READ_ONLY, 0, channel.size()); Charset charset = Charset.forName("iso-8859-1"); CharBuffer charBuffer = charset.decode(byteBuffer);
a++
c'est bien là le problème, à ce moment précis, il n'y a plus de mapping disque mais un CharBuffer chargé en mémoire... Il faudrait un MappedCharBuffer qui n'existe pas (à priori pas encore, les charsets étant trop nombreux et trop spécifiques au système d'exploitation).
Dis moi si je me trompe...
Si le sujet vous intéresse, j'avais commencé à coder des choses dans cette voie : à savoir le mapping de fichier, ça, bufferisé, et le tout gérable dans une TextArea. Faire une recherche sur MapedBufferedTextArea. Je n'ai pas avancé, mais le principe y est décrit.