読者です 読者をやめる 読者になる 読者になる

やるしかなっちゃん

やるしかない!

Javaでのビットの入出力

Javaでビットの入出力を行うのは自分で実装するしかなさそうなので実装してみた(もしかしたらちゃんとしたやり方があるのかも知れない)。
当然の前提だけど、Javaにはbit型というものは存在しないから何かで代用しなくてはいけないので、今回はbyte型をbit型として代用した。
よって書き込みは8bitずつ行うものとする(byte型の大きさは8bit)。書き込みをする操作は以下の通り。

public class FileBitIO {
    private File file;
    private byte[] bitAry;

    public FileBitIO(String fileName) {
        this.file = new File(fileName);
    }

    private byte bufferBits(byte bit, byte bitBuf, byte bufPos) {
        bit = (byte)(bit << bufPos);
        bitBuf = (byte)(bitBuf | bit);
        return bitBuf;
    }

    private void flushBits(byte bitBuf) {
        try(FileWriter filewriter = new FileWriter(file, true)) {
            printBit(bitBuf);
            filewriter.write(bitBuf);
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

    public void writeBits(byte[] bits) {
        if (bits.length > 8) {
            System.err.println("一度に書き込めるのは8bitまでだよ");
            return;
        }

        byte bitBuf = 0;
        byte bufPos = 0;
        for (int i = bits.length - 1; i >= 0; i--) {
            bitBuf = bufferBits(bits[i], bitBuf, bufPos);
            bufPos++;
        }
        flushBits(bitBuf);
    }
}

writeBitsに要素数8以下の0と1を要素に持つ配列を渡せば実際に出力できる。
例えば01000001を書き込みたい場合は次のようにすればよい。
01000001は10進数で65なので、実際に書き込んだファイルを開けば文字コードが65のAが書き込まれている。

byte[] bits = {0, 1, 0, 0, 0, 0, 0, 1};
FileBitIO fb = new FileBitIO("./hoge.txt");
fb.writeBits(bits);

書き込みを行っている処理は単純で、受け取った配列の各要素を適切なビット位置にするためにシフト演算を行い、バッファとORをとっているだけ。

書き込みは多分誰が作っても同じような感じになると思うのだけれど、読み込みが以外に悩んだ。
もう思いついたままに実装したので以下のようになった。

public class FileBitIO {
    // 省略

    private void createBytesList(List<Byte> byteList) {
        try(FileReader filereader = new FileReader(file)) {
            int b;
            while((b = filereader.read()) != -1){
                byteList.add((byte) b);
            }
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

    private void convertBitListToArray(List<Byte> byteList, byte[] bitAry) {
        int size = byteList.size();
        for (int i = 0; i < size; i++) {
            int x = byteList.get(i), bit = 1, len = 8;
            for (int j = len - 1; j >= 0; j--) {
                if ((x & bit) == 0) {
                    bitAry[(i*8)+j] = 0;
                } else {
                    bitAry[(i*8)+j] = 1;
                }
                bit <<= 1;
            }
        }
    }

    private void createBitArray() {
        List<Byte> byteList = new ArrayList<>();
        createBytesList(byteList);
        bitAry = new byte[byteList.size() * 8];
        convertBitListToArray(byteList, bitAry);
    }

    public byte[] readBits(int begin, int end) {
        if (bitAry == null) {
            createBitArray();
        }
        if (begin < 0 || end > bitAry.length || begin > end) {
            System.err.println("ダメやねん");
        }
        byte[] bits = new byte[end - begin + 1];
        System.arraycopy(bitAry, begin, bits, 0, end + 1);
        return bits;
    }
}

ファイルから1文字分読み込んでそれをリストに格納する。そして格納したそれぞれの値をビットに変換したものを配列に保存している。
readBitsはこの全ビットを保持した配列から、指定した範囲分のビットだけをコピーして返す。
ループが何箇所にも存在して非効率だとは思うけど、とりあえず動くものを作りたかったのでこうするしかなかった。
先ほどのAを書き込んだファイルから読み込んでみると以下のようになる。

FileBitIO fb = new FileBitIO("./hoge.txt");
byte[] bits = fb.readBits(0, 7);
for (int i = 0; i < bits.length; i++) {
    System.out.print(bits[i]);
}

これを実行すれば期待通りの出力01000001が得られる。

また、デバッグ用と実際に使用する時の為に以下の2つのメソッドを用意しとくと便利。

public class FileBitIO {
    // 省略

    private void printBit(byte x) {
        int bit = 1, len = 8;
        byte[] b = new byte[len];

        for (int i = 0; i < len; i++) {
            if ((x & bit) == 0) {
                b[i] = 0;
            } else {
                b[i] = 1;
            }
            bit <<= 1;
        }

        for (int i = len - 1; i >= 0; i--) {
            System.out.print(b[i]);
        }
        System.out.print(' ');
    }

    public int getBitAryLength() {
        if (bitAry == null) {
            createBitArray();
        }
        return bitAry.length;
    }

取り敢えず動くものはできたけど、果てしなく自分の実装に自信がないのでもうちょっと調べていい方法がないか試したい。