对于一个项目,我必须将二进制字符串转换为(一个数组)字节并将其写入二进制文件。
假设我有一个句子使用霍夫曼编码转换为代码字符串。例如,如果句子是:“你好” h = 00 e = 01, l = 10, o = 11
那么字符串表示将是 0001101011。
我如何将其转换为字节?<-- 如果这个问题没有意义,那是因为我对位/字节按位移位以及所有与操作 1 和 0 有关的知识知之甚少。
对于一个项目,我必须将二进制字符串转换为(一个数组)字节并将其写入二进制文件。
假设我有一个句子使用霍夫曼编码转换为代码字符串。例如,如果句子是:“你好” h = 00 e = 01, l = 10, o = 11
那么字符串表示将是 0001101011。
我如何将其转换为字节?<-- 如果这个问题没有意义,那是因为我对位/字节按位移位以及所有与操作 1 和 0 有关的知识知之甚少。
这是一个简单但可能效率低下的实现:
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class BitOutputStream extends FilterOutputStream {
private int bits = 0;
private int n = 0;
private long totalBits = 0;
public BitOutputStream(OutputStream out) {
super(out);
}
private void writeSingleBit(int bit) throws IOException {
bits = (bits << 1) | (bit & 1);
n++;
totalBits++;
if (n == 8) {
super.write(bits);
bits = 0;
n = 0;
}
}
/**
* Writes the <i>numberOfBits</i> lower bits of <i>bitsToWrite</i> to the
* output stream, starting with the most significant bit.
*/
public void writeBits(int bitsToWrite, int numberOfBits) throws IOException {
for (int i = numberOfBits - 1; i >= 0; i--) {
int bit = bitsToWrite >> i;
writeSingleBit(bit);
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
for (int i = 0; i < len; i++)
writeBits(b[off + i], 8);
}
@Override
public final void write(int b) throws IOException {
writeBits(b, 8);
}
@Override
public final void flush() throws IOException {
writeBits(0, (8 - n) & 0x07);
}
/**
* Returns the number of bits that have been written to this bitstream.
*/
public long getTotalBits() {
return totalBits;
}
}
以及相应的单元测试:
import static org.junit.Assert.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.junit.Test;
public class BitOutputStreamTest {
@Test
public void hello() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BitOutputStream bos = new BitOutputStream(baos);
bos.writeBits(0x00, 2);
bos.writeBits(0x01, 2);
bos.writeBits(0x02, 2);
bos.writeBits(0x02, 2);
bos.writeBits(0x03, 2);
assertEquals(10, bos.getTotalBits());
bos.close();
assertEquals(16, bos.getTotalBits());
assertArrayEquals(new byte[] { 0x1A, (byte) 0xC0 }, baos.toByteArray());
}
}
此代码不会以您想要的字符串表示形式输出位,但是当您稍后想将它们写入基于字节的流时,这是要走的路。
更新 (2010-09-25):修复了write(byte[], int, int)
方法中的一个错误。我忘了添加off
到数组索引。
如果您真的想要(或必须)创建位的字符串表示,您可以将字符串拆分为长度为 8 的子字符串(注意最后一个不一定长度为 8)。
Integer 具有解析字符串表示的方法,可以通过调用 radix = 2 来解析 '0' 和 '1' 的序列。
static int parseInt(String s, int radix)
将字符串参数解析为第二个参数指定的基数中的有符号整数。
--
编辑:根据评论Byte.parseByte是要走的路。
通过连接字符串表示来编码字符串表示单个字符的位序列,然后再次将其转换为字节似乎是一种非常昂贵的做事方式。
您可能想研究Preon。Preon 首先具有 BitChannel 抽象,可让您不必担心如何转换自己。您可以简单地将位序列写入 BitChannel。它将在内部跟踪“位指针”,并将所有内容转换为下游的字节。
BitChannel channel = new OutputStreamBitChannel(...);
channel.write(1, 0); // 0 = 'h'
channel.write(2, 1); // 01 = 'e'
channel.write(3, 2); // 10 = 'l'
channel.write(4, 2); // 11 = '0'
但是,理想情况下,您将能够使用 Preon 的更高级别的抽象(preon-binding),这将使您完全不必自己处理这个问题。它只需要在您的字符串上添加注释。
@BoundHuffmanCoded String toBeEncoded = "hello";
... Preon 会负责其余的工作。现在,请记住,这是理想情况,Preon还没有这个注解。但是可以自己为此注册一个编解码器。不过请留意它,因为这肯定会出现在 Preon 的未来版本中。
为什么需要先转换成“二进制字符串”?直接写字节作为输出。
从概念上讲,您所做的是将位写入 a byte
,直到填满 a byte
。这是通过位移来完成的。要在值的底部添加 1 位,您可以执行以下操作:
b = (b << 1) | 1;
然后一旦你填满了一个字节,你需要增加你的输出byte[]
为另一个字节腾出空间,直到完成。您也可以使用ByteArrayOutputStream
它来稳定输出byte
,然后再获得byte[]
。
我可以为您指出一个类,该类允许您附加位,然后稍后获取结果字节,认为它正在创建一个由 s 组成的数组int
而不是字节。您可以将其用作示例。