ByteBuffer + OutputStream: 出力ストリームのバイトオーダーを制御する方法
Javaはデフォルトでは入出力でビッグエンディアンを使用するけど、リトルエンディアン或いはその環境にネイティブのバイトオーダーを指定して操作を行ないたい時もある。
今回は出力ストリームのバイトーオーダーを制御する方法のメモ。
基本的にはByteBufferとByteOrderを組み合わせることで制御可能。
詳細は以下のクラスを参照。
※ 下のクラスはXXXOutputStreamという名前になっている癖にOutputStreamをextendsしていないけど、その辺りは必要があれば実装を修正するのは(おそらく)難しくないので放置
import java.io.OutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.Channels; import java.nio.channels.WritableByteChannel; public class ByteBufferOutputStream { static private final int CAPACITY = 8192; // ByteBufferのバッファサイズ: 適当に大きな任意の値 private WritableByteChannel channel; private ByteBuffer buffer; // sourceStream: ここにコンストラクタ内で指定されたバイトオーダーでデータが出力される。 public ByteBufferOutputStream(OutputStream sourceStream) { channel = Channels.newChannel(sourceStream); buffer = ByteBuffer.allocate(CAPACITY); // 出力ファイルのバイトオーダーを制御 buffer.order(ByteOrder.nativeOrder()); // ByteOrder.nativeOrder() or ByteOrder.LITTLE_ENDIAN or ByteOrder.BIG_ENDIAN } // 出力の途中でバイトオーダーを切り替えるためのメソッド public void changeByteOrder(int orderType) { if(orderType==0) { buffer.order(ByteOrder.nativeOrder()); // ネイティブオーダーに } else if(orderType > 0) { buffer.order(ByteOrder.BIG_ENDIAN); // ビッグエンディアンに } else { buffer.order(ByteOrder.LITTLE_ENDIAN); // リトルエンディアンに } } // バイト配列出力: エンディアンは関係ないけどByteBufferの使用方法のサンプルとして載せておく public void write(byte[] bytes) throws IOException { for(int offset=0; offset < bytes.length; offset += CAPACITY) { final int size = Math.min(CAPACITY, bytes.length-offset); buffer.position(0); // 左の二行はbuffer.clear()で置換可能(おそらく) buffer.limit(size); buffer.put(bytes, offset, size); // bytesからbufferに読み込み buffer.position(0); buffer.limit(size); channel.write(buffer); // bufferからchannel(ファイル)に書き出し } } // int値出力: buffer.order(...)で指定したバイトオーダーに従って値を出力する public void writeInt(int n) throws IOException { buffer.position(0); buffer.limit(4); buffer.putInt(n); buffer.position(0); buffer.limit(4); channel.write(buffer); } public void close() throws IOException { channel.close(); } }