将位的字符串表示转换为字节

9

我刚开始学习文件压缩,遇到了一些障碍。我的一个应用程序可以将字符串“program”编码为压缩的二进制表示形式"010100111111011000"(注意,它仍然以字符串形式存储)。

Encoding
g       111
r       10
a       110
p       010
o       011
m       00

现在我需要使用FileOutputStream将此内容写入文件系统,我的问题是,如何将字符串“010100111111011000”转换为byte[]/byte,以便使用FileOutputStream写入文件系统?
我之前从未处理过位/字节,所以我现在遇到了困难。

你提到了“压缩二进制表示”,然后说你有一个18个字符长的String(“010100111111011000”)来表示一个7个字符长的单词(“program”)。你确定你的意思是什么吗?通常情况下,你会在X个字节中设置这些位(在这种情况下为3个字节)。 - Brian Roach
搜索“位移运算符”:>>>>><< - Kevin
Brian,原始消息在转换为二进制时大小为56位,编码后的消息仅为18位。Kevin,人们一直告诉我这个,但我仍然无法将使用这些运算符与能够将其转换为字节数组联系起来。 - John Lotacs
@JohnLotacs - 不是的,如果你在谈论你在问题中提到的String,那么这就是混淆的根源。如果你有一个String,你没有比特位。你有一堆字符01(具体来说,你有每个字符16位Unicode字符,使得你的内存使用36字节,在String对象的开销之前)- 明确地说,如果你有一个String,你有一组比特位的文本表示,使用字符0和1表示。 - Brian Roach
Brian,那就是问题了,将位的字符串表示转换为一组字节。 - John Lotacs
显示剩余3条评论
3个回答

6

位移运算符简介:

首先,我们有左移运算符 x << n。这将把x中所有的位向左移动n位,并用零填充新的位:

      1111 1111 
<< 3: 1111 1000

接下来介绍有符号右移运算符x >> n。这将右移x中的所有位n位,将符号位复制到新位中:

      1111 1111 
>> 3: 1111 1111

      1000 0000
>> 3: 1111 0000

      0111 1111 
>> 3: 0000 1111

最后,我们有了零填充右移操作符 x >>> n。它将x中的所有位向右移动n位,并用零填充新位:

       1111 1111 
>>> 3: 0001 1111

您还可能会发现按位或运算符x | y非常有用。它比较xy中每个位置上的位,如果在xy中任意一个位上为1,则设置新数字的该位为1,否则为0:

  1010 0101
| 1010 1010
  ---------
  1010 1111

你只需要使用前面的运算符来解决问题,但为了完整起见,这里提供最后两个运算符:
按位与运算符x & y仅当xy中的位都为1时才将输出位设置为1:
  1010 0101
& 1010 1010
  ---------
  1010 0000

按位异或运算符x ^ y,如果两个数中的某一位只有一个为1,则将输出位设置为1:
  1010 0101
^ 1010 1010
  ---------
  0000 1111

现在,将这些应用到手头的情况:
您需要使用位移运算符来添加和操作位。按照它们的字符串表示从右侧开始设置位并将它们移位。一直持续到达字节末尾,然后移动到下一个字节。假设我们想要创建“1100 1010”的字节表示:
Our byte    Target
---------   --------
0000 0000
            1100 1010
0000 0001   ^
            1100 1010
0000 0011    ^
            1100 1010
0000 0110     ^
            1100 1010
0000 1100      ^
            1100 1010
0001 1001        ^
            1100 1010
0011 0010         ^
            1100 1010
0110 0101          ^
            1100 1010
1100 1010           ^

当然,我会让您自行将其应用于您的工作中。


一个问题,如果我想将我的字节以0000 0001的格式开始,这是否等同于写入byte b = 1;?我不确定,因为byte具有带符号的特性,我不知道二进制表示法是什么,因为我不知道哪一位代表符号。 - John Lotacs
你可以这样做,但为了保持一致性,你需要从零字节开始,然后进入一个forwhile循环。我会稍微编辑一下示例,看看能否让它更清晰一些。 - Kevin

1

将你的String切成长度为8的片段,然后调用Byte#parseByte。如果将radix设置为2,它将解析String作为二进制数。


1
主线程中的异常:"main" java.lang.NumberFormatException: 值超出范围。值:"10000000" 进制:2 它仅适用于长度为7的数字,除非有前导零,你有什么想法吗? - John Lotacs
@John Lotacs,我不知道为什么会这样,但你可以使用Integer#parseInt并将其转换为byte来解决问题。 - Jeffrey
@jeff 这是因为 byte 是有符号的,所以它需要在 -111 1111+111 1111(-128 到 +127)之间。一个二进制位为 1000 0000 的字节实际上是 -128,必须作为 -1000 0000 提供给解析器。 - Kevin
@Kevin 为什么它不能直接取 1000 000 呢?这是程序员懒惰的表现还是我漏掉了什么东西? - Jeffrey
parseByte 方法解析文本的值,而不是单个位。1000 0000 是 128,超出了 byte 的最大值 127。它对于 unsigned byte 来说是在范围内的,但 Java 没有无符号类型(除了 char)。 - Kevin
@Kevin 啊,现在我明白了。是的,char 是无符号的。 - Jeffrey

0

我猜你想把这些0和1写成二进制值存入文件中。如果是这样,你可以迭代字符串,每次取8个字符(使用String.substring()或其他方法),并使用Byte(String)构造函数创建字节。

这是我目前想到的最简单的解决方案。

如果我的猜测不正确,请提供更多信息。


我尝试过这个,Byte(String)构造函数将会把字符串"0011"直接解释为十进制数11。 - John Lotacs
这就是为什么你应该使用Byte(String s, int radix)构造函数来设置二进制基数。 - Jakub Matczak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接