Question

I am little new to bit level reading and writing to/from file.

I want to read and write to file with precision at bit level. i.e. read exactly same number of bits in buffer as it is written.

Here is my attempt:

1) Write to file method

private static final int INT_BYTES = Integer.SIZE / Byte.SIZE;
public void writeToFile(FileChannel fc, BitSet bitSet, int intId ) throws IOException 
{
  //each bitSet has a intId, first two int bytes written will have intId and bitSet.length() and rest will have content of bitSet

  int byteLenOfBitSet=(int)Math.ceil((bitSet.length/8.0)); 

  ByteBuffer bf = ByteBuffer.allocate(byteLenOfBitSet + 2*(INT_BYTES));
  bf.putInt(intId); //put the Id    
  bf.putInt(bitSet.length(); // put the bit length, it would be used during read
  bf.put(bitSet) //FIXME this is wrong, need to put bits , bf.put() put bytes               
  bf.flip();
  fc.write(bf);

}

2) Read from file method

   public Result readFromFile(FileChannel fc) throws IOException 
    {
      long currentPos = fc.position();
      ByteBuffer bf = ByteBuffer.allocate(2* INT_BYTES);

      if(fc.read(bf) < 0)return null;
      bf.rewind();
      int intId=bf.getInt();    //read first int as intId
      int bitLen = bf.getInt(); //read second int as bitLen to read further from file   
      int byteLen=(int)Math.ceil((bitLen/8.0));  //FIXME not so sure

      //move fc read position ahead by 2 INT_BYTES to read bitSet
      fc.position((currentPos + INT_BYTES * 2));        
      bf = ByteBuffer.allocate(byteLen);//FIXME, this is wrong, we need to somehow get bit not in bytelen , don't want unnecessarily read entire byte if there is less than 8 bit of info to read            

     if(fc.read(bf) < 0)return null;

     bf.rewind();
     BitSet readBitSet = new BitSet();

     //TODO, then read each bit from bf to a readBitSet
     // and return result with intId+readBitSet   

   }

In another set of methods, where I had to read and write just Integers (at byte level) I had got it working fine using logic similar to above. But, got stuck at bit level.

Please let me know if need more clarification.

It might be similar to Read and write file bit by bit but that answer is for Perl, I am looking for implementation in Java

EDIT

My concern:

Since data are written to file this way

2 INT_BYTES then bitSet   example: 5 3       101
2 INT_BYTES then bitSet   example: 2 10 1010111101 

I am concerned I might read second 2 INT_BYTES while trying to read first bitSet, so my first result bitSet would be wrong. So, wondering how to ensure that bit level boundary is maintained. i.e. I want to read until first BitSet's length only when reading first bitSet.

Was it helpful?

Solution

This answer includes a BitSet subclass that has a toByteArray method. To write, you can get the byte[] array from that method and use ByteBuffer.put(byte[]) (ByteBuffer docs). To read, you can use get() and then loop over the byte[] and rebuild the bitset.

(For reference: FileChannel docs)

EDIT in answer to your question below.

  • I think you can get rid of fc.position since fc.read and bf.getInt both advance their current positions.

  • According to this answer, the parameter to allocate should be the number of bytes you want to read with a call to fc.read. So 2*INT_BYTES looks correct for the first allocate call. The second allocate also looks OK; just don't call fc.position.

  • For byteLen, try byteLen=(bitLen >> 3) + ((bitLen&0x07)?1:0). bitLen>>3 divides by 8 (2^3) with truncation. So 1..7 bits have zero, 8..15 have one, ... . If the number of bits is not a multiple of 8, you thus need one more byte. ((bitLen&0x07)?1:0) is 1 in that situation and 0 otherwise.

Bear in mind that the bits will be padded at the end if you don't have a multiple of 8. E.g., reading 12 bits will take two full bytes from the stream.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top