是否可以使用JAVA从文件读取/写入位?

Ete*_*oob 8 java file-io bit-manipulation

要读/写二进制文件,我使用DataInputStream/DataOutputStream,他们有这个方法writeByte()/ readByte(),但我想做的是读/写位?可能吗?

我想用它来进行压缩算法,所以当我压缩时我想写3位(一个数字,文件中有数百万这样的数字),如果我每次写一个字节,我需要写3位,我会写大量的冗余数据......

nos*_*nos 8

直接读/写单​​个位是不可能的,您可以读/写的最小单位是一个字节.

你可以使用标准的按位运算符来操作一个字节,所以例如要获得一个字节的最低2位,你可以做

byte b = in.readByte();
byte lowBits = b&0x3;
Run Code Online (Sandbox Code Playgroud)

将低4位设置为1,并写入字节:

b |= 0xf;
out.writeByte(b);
Run Code Online (Sandbox Code Playgroud)

(注意,为了提高效率,您可能希望读/写字节数组而不是单字节)

  • 实际上我认为这不是解决方案,因为当您只想写入三位时(根据要求),您无法写入一个字节。您必须缓冲位,直到至少有一个字节已满。那部分不见了。 (2认同)

M.J*_*urn 6

没有办法直接做到.计算机可以处理的最小单位是一个字节(甚至布尔值占用一个字节).但是,您可以创建一个自定义流类,该类使用您想要的位打包一个字节然后写入它.然后你可以为这个类创建一个包装器,它的写函数采用一些整数类型,检查它是否在0到7之间(或-4和3 ......或者其他),以与BitInputStream类相同的方式提取位(如下)做,并对BitOutputStream的write方法进行相应的调用.您可能认为您可以只生成一组IO流类,但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();
    }

}
Run Code Online (Sandbox Code Playgroud)

我确信有一种方法可以用逐位运算符打包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();
    }

}
Run Code Online (Sandbox Code Playgroud)

您可能知道这一点,但是您应该在BitStream和FileStream之间放置一个BufferedStream,否则它将永远消失.

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

}
Run Code Online (Sandbox Code Playgroud)

  • 恐怕使用布尔数组来表示位的效率相当低。 (2认同)

Jin*_*won 5

请查看我的 bit-io 库https://github.com/jinahya/bit-io,它可以读取和写入非八位字节对齐的值,例如 1 位布尔值或 17 位无符号整数。

<dependency>
  <!-- resides in central repo -->
  <groupId>com.googlecode.jinahya</groupId>
  <artifactId>bit-io</artifactId>
  <version>1.0-alpha-13</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

该库读取和写入任意长度的位。

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
Run Code Online (Sandbox Code Playgroud)