2010-11-18 37 views
5

Pour lire/écrire des fichiers binaires, j'utilise DataInputStream/DataOutputStream, ils ont cette méthode writeByte()/readByte(), mais ce que je veux faire est de lire/écrire des bits? C'est possible? Je veux l'utiliser pour un algorithme de compression, donc quand je suis en train de compresser je veux écrire 3 bits (pour un nombre et il y a des millions de tels nombres dans un fichier) et si j'écris un octet à chaque fois que j'ai besoin pour écrire 3 bits, je vais écrire des charges de données redondantes ...Est-il possible de lire/écrire des bits à partir d'un fichier en utilisant JAVA?

Répondre

5

Il n'est pas possible de lire/écrire directement des bits individuels, la plus petite unité que vous pouvez lire/écrire est un octet.

Vous pouvez utiliser les opérateurs standard bitwise pour manipuler un octet, par exemple. pour obtenir les plus bas 2 bits d'un octet, vous feriez

byte b = in.readByte(); 
byte lowBits = b&0x3; 

définissez les basses 4 bits à 1, et d'écrire l'octet:

b |= 0xf; 
out.writeByte(b); 

(Notez, par souci d'efficacité, vous pourrait vouloir lire/écrire des tableaux d'octets, et non simples octets)

+0

Il semble que je vais devoir lire/écrire des octets et apprendre des opérations au niveau du bit ... Merci à tous pour les réponses .... –

+0

Merci nos, pour le point d'efficacité ... –

+0

Par anding b et 0x3, aren Est-ce que seuls les 2 bits les moins significatifs sont conservés en lowbits? –

2

InputStreams et OutputStreams sont des flux d'octets.

Pour lire un bit, vous devez lire un octet, puis utiliser la manipulation de bits pour inspecter les bits qui vous intéressent. De même, pour écrire des bits, vous devez écrire des octets contenant les bits que vous voulez.

3

Oui et non. Sur la plupart des ordinateurs modernes, un octet est la plus petite unité de mémoire adressable, de sorte que vous ne pouvez lire/écrire que des octets entiers à la fois. Cependant, vous pouvez toujours utiliser des opérateurs au niveau du bit pour manipuler les bits dans un octet.

1

Les bits sont empaquetés en octets et en dehors de VHDL/Verilog, je n'ai vu aucun langage qui vous permet d'ajouter des bits individuels à un flux. Mettre en cache vos bits et les emballer dans un octet pour une écriture en utilisant un tampon et bitmasking. Faites l'inverse pour la lecture, c'est-à-dire conservez un pointeur dans votre tampon et augmentez-le lorsque vous renvoyez des bits masqués individuellement.

1

Afaik il n'y a pas de fonction pour ce faire dans l'API Java. Cependant, vous pouvez bien sûr lire un octet, puis utiliser des fonctions de manipulation de bits. Idem pour l'écriture.

1

Transféré à https://github.com/jinahya/bit-io

S'il vous plaît jeter un oeil àCeci est une petite bibliothèque pratique pour lire/écrire des longueurs de bits arbitraires avec Java.

final InputStream stream; 
final BitInput input = new BitInput(new BitInput.StreamInput(stream)); 

final int b = input.readBoolean(); // reads a 1-bit boolean value 
final int i = input.readUnsignedInt(3); // reads a 3-bit unsigned int 
final long l = input.readLong(47); // reads a 47-bit signed long 

input.align(1); // 8-bit byte align; padding 


final WritableByteChannel channel; 
final BitOutput output = new BitOutput(new BitOutput.ChannelOutput(channel)); 

output.writeBoolean(true); // writes a 1-bit boolean value 
output.writeInt(17, 0x00); // writes a 17-bit signed int 
output.writeUnsignedLong(54, 0x00L); // writes a 54-bit unsigned long 

output.align(4); // 32-bit byte align; discarding 
4

Il n'y a aucun moyen de le faire directement. La plus petite unité qu'un ordinateur peut gérer est un octet (même les booléens prennent un octet). Cependant, vous pouvez créer une classe de flux personnalisée qui emballe un octet avec les bits que vous voulez, puis l'écrit. Vous pouvez ensuite créer un wrapper pour cette classe dont la fonction write prend un type entier, vérifie qu'il est compris entre 0 et 7 (ou -4 et 3 ... ou autre), extrait les bits de la même manière que la classe BitInputStream (ci-dessous) fait, et effectue les appels correspondants à la méthode d'écriture de BitOutputStream. Vous pourriez penser que vous pourriez juste faire un ensemble de classes de flux IO, mais 3 ne va pas dans 8 même.Donc, si vous voulez une efficacité de stockage optimale et que vous ne voulez pas travailler très dur, vous êtes coincé avec deux couches d'abstraction. Voici une classe BitOutputStream, une classe BitInputStream correspondante et un programme qui vérifie qu'ils fonctionnent.

import java.io.IOException; 
import java.io.OutputStream; 

class BitOutputStream { 

    private OutputStream out; 
    private boolean[] buffer = new boolean[8]; 
    private int count = 0; 

    public BitOutputStream(OutputStream out) { 
     this.out = out; 
    } 

    public void write(boolean x) throws IOException { 
     this.count++; 
     this.buffer[8-this.count] = x; 
     if (this.count == 8){ 
      int num = 0; 
      for (int index = 0; index < 8; index++){ 
       num = 2*num + (this.buffer[index] ? 1 : 0); 
      } 

      this.out.write(num - 128); 

      this.count = 0; 
     } 
    } 

    public void close() throws IOException { 
     int num = 0; 
     for (int index = 0; index < 8; index++){ 
      num = 2*num + (this.buffer[index] ? 1 : 0); 
     } 

     this.out.write(num - 128); 

     this.out.close(); 
    } 

} 

Je suis sûr qu'il ya un moyen d'emballer l'int avec les opérateurs bit à bit et ainsi éviter d'avoir à inverser l'entrée, mais je ne pas quoi penser que dur.

En outre, vous avez probablement remarqué qu'il n'y a aucun moyen local pour détecter que le dernier bit a été lu dans cette mise en œuvre, mais je vraiment ne veux pas penser que dur.

import java.io.IOException; 
import java.io.InputStream; 

class BitInputStream { 

    private InputStream in; 
    private int num = 0; 
    private int count = 8; 

    public BitInputStream(InputStream in) { 
     this.in = in; 
    } 

    public boolean read() throws IOException { 
     if (this.count == 8){ 
      this.num = this.in.read() + 128; 
      this.count = 0; 
     } 

     boolean x = (num%2 == 1); 
     num /= 2; 
     this.count++; 

     return x; 
    } 

    public void close() throws IOException { 
     this.in.close(); 
    } 

} 

Vous le savez probablement, mais vous devriez mettre un BufferedStream entre votre Bitstream et FileStream ou il va prendre une éternité.

import java.io.BufferedInputStream; 
import java.io.BufferedOutputStream; 
import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.util.Random; 

class Test { 

    private static final int n = 1000000; 

    public static void main(String[] args) throws IOException { 

     Random random = new Random(); 

     //Generate array 

     long startTime = System.nanoTime(); 

     boolean[] outputArray = new boolean[n]; 
     for (int index = 0; index < n; index++){ 
      outputArray[index] = random.nextBoolean(); 
     } 

     System.out.println("Array generated in " + (double)(System.nanoTime() - startTime)/1000/1000/1000 + " seconds."); 

     //Write to file 

     startTime = System.nanoTime(); 

     BitOutputStream fout = new BitOutputStream(new BufferedOutputStream(new FileOutputStream("booleans.bin"))); 

     for (int index = 0; index < n; index++){ 
      fout.write(outputArray[index]); 
     } 

     fout.close(); 

     System.out.println("Array written to file in " + (double)(System.nanoTime() - startTime)/1000/1000/1000 + " seconds."); 

     //Read from file 

     startTime = System.nanoTime(); 

     BitInputStream fin = new BitInputStream(new BufferedInputStream(new FileInputStream("booleans.bin"))); 

     boolean[] inputArray = new boolean[n]; 
     for (int index = 0; index < n; index++){ 
      inputArray[index] = fin.read(); 
     } 

     fin.close(); 

     System.out.println("Array read from file in " + (double)(System.nanoTime() - startTime)/1000/1000/1000 + " seconds."); 

     //Delete file 
     new File("booleans.bin").delete(); 

     //Check equality 

     boolean equal = true; 
     for (int index = 0; index < n; index++){ 
      if (outputArray[index] != inputArray[index]){ 
       equal = false; 
       break; 
      } 
     } 

     System.out.println("Input " + (equal ? "equals " : "doesn't equal ") + "output."); 
    } 

} 
+0

Vous devez savoir que si le nombre de bits n'est pas un multiple de 8, le dernier octet sera complété avec des zéros en tête. Cela peut causer des bogues si ce n'est pas géré. – Gregory

0

Le code ci-dessous devrait fonctionner

int[] mynumbers = {3,4}; 
    BitSet compressedNumbers = new BitSet(mynumbers.length*3); 
    // let's say you encoded 3 as 101 and 4 as 010 
    String myNumbersAsBinaryString = "101010"; 
    for (int i = 0; i < myNumbersAsBinaryString.length(); i++) { 
     if(myNumbersAsBinaryString.charAt(i) == '1') 
      compressedNumbers.set(i); 
    } 
    String path = Resources.getResource("myfile.out").getPath(); 
    ObjectOutputStream outputStream = null; 
    try { 
     outputStream = new ObjectOutputStream(new FileOutputStream(path)); 
     outputStream.writeObject(compressedNumbers); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
0

Si vous êtes en train d'écrire bits à un fichier, Java de BitSet class peut-être vaut un coup d'oeil. De la javadoc:

Cette classe implémente un vecteur de bits qui se développe selon les besoins. Chaque composant de l'ensemble de bits a une valeur booléenne. Les bits d'un BitSet sont indexés par des entiers non négatifs. Les bits indexés individuels peuvent être examinés, définis ou effacés. Un BitSet peut être utilisé pour modifier le contenu d'un autre BitSet via des opérations OR logiques, logiques OU inclusives et OU logiques exclusives.

Vous pouvez convertir BitSets en long [] et byte [] pour enregistrer des données dans un fichier.

+0

Comment 'BitSet' est-il pertinent ici? –