2010-11-18 104 views
5

要讀取/寫入二進制文件,我使用DataInputStream/DataOutputStream,他們有這種方法writeByte()/ readByte(),但我想要做的是讀/寫位?可能嗎?我想將它用於壓縮算法,所以當我壓縮時我想寫3位(對於一個數字,並且文件中有數百萬個這樣的數字),並且如果我每次都需要寫一個字節寫3位,我將寫入大量的冗餘數據...是否可以使用JAVA從文件中讀取/寫入位?

回答

5

無法直接讀取/寫入各個位,您可以讀取/寫入的最小單位是一個字節。

您可以使用標準的bitwise運算符來操縱一個字節,例如,得到一個字節的最低2位,你會做

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

設置低4位爲1,並寫入字節:

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

(注意,出於效率的考慮你可能要讀取/寫入的字節數組,而不是單個字節)

+0

看來,我將不得不讀取/寫入字節和學習按位操作...謝謝大家的答案.... – 2010-11-19 00:11:49

+0

謝謝你,爲效率點... – 2010-11-19 00:12:13

+0

通過和b和0x3,aren只有2個最低有效位保存在低位? – 2016-04-02 07:18:54

2

InputStreams和OutputStreams是字節流。

要讀取一個位,您需要讀取一個字節,然後使用位操作來檢查您關心的位。同樣,要寫位,你需要寫出包含你想要的位的字節。

3

是和否。在大多數現代計算機上,一個字節是最小的可尋址內存單元,因此您一次只能讀取/寫入整個字節。但是,您始終可以使用按位運算符來操作一個字節內的位。

1

位是以字節爲單位打包的,除了VHDL/Verilog之外,我沒有看到允許您將單個位附加到流中的語言。緩存你的位,並使用緩衝區將它們打包成一個字節進行寫操作,並且使用bitmasking。做相反的讀操作,即在你的緩衝區中保留一個指針,並且當你返回單獨的被屏蔽位時增加它。

1

Afaik在Java API中沒有這樣做的功能。但是,您當然可以讀取一個字節,然後使用位操作功能。寫作也一樣。

1

移到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 
4

沒有辦法直接做。計算機可以處理的最小單位是一個字節(甚至布爾值佔用一個字節)。但是,您可以創建一個自定義的流類,它可以將需要寫入的位打包爲一個字節。然後,你可以爲這個類編寫一個包裝函數,它的write函數需要一些整型,檢查它是在0到7之間(或者-4和3 ......或者其他),以與BitInputStream類(下面)相同的方式提取這些位,並且對BitOutputStream的寫入方法進行相應的調用。你可能會認爲你只能創建一組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(); 
    } 

} 

你可能知道這一點,但你應該把一個BufferedStream在比特流和之間的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."); 
    } 

} 
+0

您必須知道,如果位數不是8的倍數,則最後一個字節將以前導零完成。如果沒有處理它可能會導致一些錯誤。 – Gregory 2016-09-22 15:08:31

0

下面的代碼應該工作

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

如果你只是寫位到一個文件中,Java的BitSet class可能是值得看一看。來自javadoc:

該類實現了根據需要增長的位向量。位集的每個組件都有一個布爾值。 BitSet的位由非負整數索引。各個索引位可以被檢查,設置或清除。一個BitSet可用於通過邏輯AND,邏輯或(OR)和邏輯異或操作來修改另一個BitSet的內容。

您可以將BitSets轉換爲long []和byte []將數據保存到文件。

+0

'BitSet'在這裏如何相關? – 2016-04-02 11:11:53

相關問題