Можно ли читать / записывать биты из файла, используя JAVA? - PullRequest
7 голосов
/ 19 ноября 2010

Для чтения / записи двоичных файлов я использую DataInputStream / DataOutputStream, у них есть этот метод writeByte () / readByte (), но что я хочу сделать, это биты чтения / записи?Является ли это возможным?

Я хочу использовать его для алгоритма сжатия, поэтому, когда я сжимаю, я хочу записать 3 бита (для одного числа, а в файле миллионы таких чисел), и если я пишу байт каждый разМне нужно написать 3 бита, я напишу множество избыточных данных ...

Ответы [ 9 ]

6 голосов
/ 19 ноября 2010

Невозможно читать / записывать отдельные биты напрямую, наименьшая единица, которую вы можете прочитать / записать, - это байт.

Вы можете использовать стандартные побитовые операторы для манипулирования байтом, например, например. чтобы получить младшие 2 бита, вы должны сделать

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

установите младшие 4 бита в 1 и запишите байт:

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

(Обратите внимание, ради эффективности вы можете читать / записывать байтовые массивы, а не отдельные байты)

5 голосов
/ 23 сентября 2014

Нет способа сделать это напрямую. Самая маленькая единица, которую компьютеры могут обрабатывать, - это байт (даже логические значения занимают байт). Однако вы можете создать собственный класс потока, который упаковывает байт с нужными битами, а затем записывает его. Затем вы можете сделать обертку для этого класса, функция записи которой принимает некоторый целочисленный тип, проверяет, что он находится между 0 и 7 (или -4 и 3 ... или что-то еще), извлекает биты таким же образом, как класс BitInputStream (ниже) делает и делает соответствующие вызовы метода записи BitOutputStream. Вы можете подумать, что вы могли бы просто сделать один набор классов потоков ввода-вывода, но 3 не входит в 8 равномерно. Так что, если вам нужна оптимальная эффективность хранения и вы не хотите работать усердно, вы застряли с двумя уровнями абстракции. Ниже приведен класс BitOutputStream, соответствующий класс BitInputStream и программа, обеспечивающая их работу.

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

}

Я уверен, что есть способ упаковать int с помощью побитовых операторов и, таким образом, избежать необходимости переворачивать ввод, но я не думаю, что так сложно думать.

Кроме того, вы, вероятно, заметили, что нет локального способа определить, что последний бит был прочитан в этой реализации, но я действительно не хочу думать , что трудно .

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

}

Возможно, вы это знаете, но вам следует поместить BufferedStream между вашим BitStream и FileStream, иначе это займет вечность.

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.");
    }

}
2 голосов
/ 19 ноября 2010

Да и нет. На большинстве современных компьютеров байт является наименьшей адресуемой единицей памяти, поэтому вы можете читать / записывать только целые байты за раз. Однако вы всегда можете использовать побитовые операторы для манипулирования битами внутри байта.

2 голосов
/ 19 ноября 2010

InputStreams и OutputStreams являются потоками байтов.

Чтобы прочитать бит, вам нужно прочитать байт, а затем использовать битовую манипуляцию для проверки битов, которые вам нужны.Аналогично, для записи битов вам нужно записать байты, содержащие нужные биты.

1 голос
/ 01 февраля 2016

Если вы просто записываете биты в файл, класс Java BitSet может стоить посмотреть. Из Javadoc:

Этот класс реализует вектор битов, который увеличивается по мере необходимости. Каждый компонент набора битов имеет логическое значение. Биты BitSet индексируются неотрицательными целыми числами. Отдельные индексированные биты могут быть проверены, установлены или очищены. Один BitSet может использоваться для изменения содержимого другого BitSet с помощью логического И, логического включающего ИЛИ и логического исключающего ИЛИ.

Вы можете конвертировать битовые наборы в long [] и byte [] для сохранения данных в файл.

1 голос
/ 28 мая 2013

Перемещено в https://github.com/jinahya/bit-io

Пожалуйста, посмотрите на 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>

Это небольшая удобная библиотека для чтения / записи произвольной длины битов в 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
1 голос
/ 19 ноября 2010

Афаик, в Java API нет функции для этого.Однако вы, конечно, можете прочитать байт, а затем использовать функции управления битами.То же самое касается письма.

1 голос
/ 19 ноября 2010

Биты упакованы в байты, и кроме VHDL / Verilog я не видел ни одного языка, который позволял бы добавлять отдельные биты в поток. Кэшируйте свои биты и упаковывайте их в байт для записи, используя буфер и битовую маску . Сделайте обратное для чтения, то есть сохраните указатель в буфере и увеличивайте его, когда вы возвращаете индивидуально замаскированные биты.

0 голосов
/ 11 января 2016

Код ниже должен работать

    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();
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...