2010-11-18 17 views
5

Para leer/escribir archivos binarios, estoy usando DataInputStream/DataOutputStream, tienen este método writeByte()/readByte(), pero lo que quiero hacer es leer/escribir bits? ¿Es posible?¿Es posible leer/escribir bits de un archivo usando JAVA?

Quiero usarlo para un algoritmo de compresión, así que cuando estoy comprimiendo quiero escribir 3 bits (para un número y hay millones de dichos números en un archivo) y si escribo un byte en cada momento que necesito para escribir 3 bits, escribiré un montón de datos redundantes ...

Respuesta

5

No es posible leer/escribir bits individuales directamente, la unidad más pequeña que puede leer/escribir es un byte.

Puede utilizar los operadores estándar bitwise para manipular un byte, por lo tanto, p. para obtener los 2 bits más bajos de un byte, que haría

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

establezca las bajas 4 bits a 1, y escribir el byte:

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

(Tenga en cuenta, en aras de la eficiencia se puede ser que desee para leer/escribir las matrices de bytes y bytes no individuales)

+0

Parece que voy a tener que leer los bytes/escritura y aprender operaciones bit a bit ... Gracias a todos por las respuestas .... –

+0

Gracias nos, para el punto de eficiencia ... –

+0

Por anding b y 0x3, aren ¿Solo se conservan los 2 bits menos significativos en personas de bajos recursos? –

2

InputStreams y OutputStreams son flujos de bytes.

Para leer un poco necesitarás leer un byte y luego usar la manipulación de bits para inspeccionar los bits que te interesan. Del mismo modo, para escribir bits necesitarás escribir bytes que contengan los bits que deseas.

3

Sí y no. En la mayoría de las computadoras modernas, un byte es la unidad de memoria direccionable más pequeña, por lo que solo puede leer/escribir bytes completos a la vez. Sin embargo, siempre puede usar operadores bit a bit para manipular los bits dentro de un byte.

1

Los bits se empaquetan en bytes y, aparte de VHDL/Verilog, no he visto ningún idioma que le permita agregar bits individuales a una secuencia. Guarde en caché sus bits y empaquételos en un byte para una escritura usando un buffer y bitmasking. Haga lo contrario para leer, es decir, mantenga un puntero en su búfer e increméntelo a medida que devuelve bits individualmente enmascarados.

1

Afaik no hay ninguna función para hacer esto en la API de Java. Sin embargo, puede, por supuesto, leer un byte y luego usar las funciones de manipulación de bits. Lo mismo aplica para escribir

1

Se traslada a https://github.com/jinahya/bit-io

favor, eche un vistazo a http://jinahya.googlecode.com/svn/trunk/com.googlecode.jinahya/bit-io/src/main/java/com/googlecode/jinahya/io/

<dependency> 
    <!-- resides in central repo --> 
    <groupId>com.googlecode.jinahya</groupId> 
    <artifactId>bit-io</artifactId> 
    <version>1.0-alpha-13</version> 
</dependency> 

Esta es una pequeña biblioteca útil para leer/escribir bits de longitud arbitraria con 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

No hay forma de hacerlo directamente. La unidad más pequeña que las computadoras pueden manejar es un byte (incluso los booleanos ocupan un byte). Sin embargo, puede crear una clase de flujo personalizado que empaqueta un byte con los bits que desea y luego lo escribe. A continuación, puede crear un contenedor para esta clase cuya función de escritura adopta un tipo integral, verifica que esté entre 0 y 7 (o -4 y 3 ... o lo que sea), extrae los bits de la misma manera que la clase BitInputStream (abajo) hace, y hace las llamadas correspondientes al método de escritura de BitOutputStream. Tal vez piense que podría hacer un solo conjunto de clases de flujo IO, pero 3 no pasa a 8 de manera uniforme.Entonces, si desea una eficiencia de almacenamiento óptima y no desea trabajar realmente duro, está atrapado con dos capas de abstracción. A continuación se muestra una clase BitOutputStream, una clase correspondiente BitInputStream y un programa que se asegura de que funcionen.

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

} 

estoy seguro de que hay una manera de empaquetar el int con los operadores bit a bit y así evitar tener que invertir la entrada, pero no lo sé qué pensar tan difícil.

También, usted probablemente ha notado que no hay manera local para detectar que el último bit se ha leído en esta implementación, pero realmente no quiere pensar que duro.

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

} 

usted probablemente sabe esto, pero usted debe poner un BufferedStream en medio de su BitStream y FileStream o se va a tomar para siempre.

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

Debe saber que si el número de bits no es un múltiplo de 8, el último byte se completará con ceros iniciales. Puede causar algunos errores si no se maneja. – Gregory

0

El siguiente código debería funcionar

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 se acaba de escritura de bits en un archivo, Java de BitSet class podría valer la pena un vistazo. Desde javadoc:

Esta clase implementa un vector de bits que crece según sea necesario. Cada componente del conjunto de bits tiene un valor booleano. Los bits de un BitSet están indexados por enteros no negativos. Los bits indizados individuales se pueden examinar, configurar o borrar. Un BitSet se puede usar para modificar los contenidos de otro BitSet a través de operaciones lógicas AND, lógicas o inclusivas, y lógicas O exclusivas.

Puede convertir los BitSets a long [] y byte [] para guardar datos en un archivo.

+0

¿cómo es 'BitSet' relevante aquí? –

Cuestiones relacionadas