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

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

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

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

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

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

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

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

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

没有办法直接做到这一点。 计算机可以处理的最小单位是一个字节(甚至布尔值占用一个字节)。 但是,您可以创建一个自定义流类,使用您想要的位打包一个字节然后写入它。 然后你可以为这个类创建一个包装器,它的写函数需要一些整数类型,检查它是否在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(); } } 

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

您可能知道这一点,但是您应该在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."); } } 

InputStreams和OutputStreams是字节流。

要读取一点,您需要读取一个字节,然后使用位操作来检查您关心的位。 同样,要写入位,您需要写入包含所需位的字节。

是的,不是。 在大多数现代计算机上,字节是内存的最小可寻址单位,因此您一次只能读/写整个字节。 但是,您始终可以使用按位运算符来操作一个字节内的位。

比特以字节打包,除了VHDL / Verilog之外,我没有看到任何允许您将单个位附加到流的语言。 缓存您的位并将它们打包成一个字节,以便使用缓冲区和位掩码进行写操作。 执行相反的操作,即将指针保留在缓冲区中,并在单独返回屏蔽位时递增指针。

Afaik在Java API中没有执行此操作的function。 但是,您当然可以读取一个字节然后使用位操作函数。 写作也一样。

已移至https://github.com/jinahya/bit-io

请查看http://jinahya.googlecode.com/svn/trunk/com.googlecode.jinahya/bit-io/src/main/java/com/googlecode/jinahya/io/

   com.googlecode.jinahya bit-io 1.0-alpha-13  

这是一个小型的便捷库,用于使用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 

以下代码应该有效

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

如果您只是将位写入文件,Java的BitSet类可能值得一看。 来自javadoc:

该类实现了一个根据需要增长的位向量。 位组的每个组件都有一个布尔值。 BitSet的位由非负整数索引。 可以检查,设置或清除各个索引位。 一个BitSet可用于通过逻辑AND,逻辑包含OR和逻辑异或运算来修改另一个BitSet的内容。

您可以将BitSet转换为long []和byte []以将数据保存到文件中。