【问题标题】:Can you explain the bits I'm getting from unpack?你能解释一下我从 unpack 中得到的信息吗?
【发布时间】:2011-08-10 14:12:17
【问题描述】:

我对 Perl 相对缺乏经验,但我的问题是在获取数值位时的解包函数。例如:

my $bits = unpack("b*", 1);
print $bits;

这将打印 10001100,即十进制的 140。在相反的顺序中,它是十进制的 49。我尝试过的任何其他值似乎都给出了不正确的位。

但是,当我通过 pack 运行 $bits 时,它再次产生 1。我这里有什么遗漏吗?

当我认为我的问题已经解决时,我似乎仓促下结论。也许我应该简要解释一下我正在尝试做什么。

我需要将一个可能长达 24 位(关键是它可能大于一个字节)的整数值转换为位串。这可以使用@ikegami 建议的解包和打包来完成,但我还需要找到一种方法将该位字符串转换回它的原始整数(不是它的字符串表示形式)。

正如我所提到的,我对 Perl 相对缺乏经验,而且我一直在尝试但没有成功。


我发现似乎是最佳解决方案:

my $bits = sprintf("%032b", $num);
print "$bits\n";
my $orig = unpack("N", pack("B32", substr("0" x 32 . $bits, -32)));
print "$orig\n";

【问题讨论】:

    标签: perl bits pack unpack


    【解决方案1】:

    这可能很明显,但其他答案并没有明确指出:unpack("b*", 1) 中的第二个参数被类型转换为字符串"1",它的 ASCII 值是十六进制的31(带有首先是最重要的半字节)。

    对应的二进制文件是00110001,在你的输出中它被反转为10001100,因为你使用了"b*"而不是"B*"。这些对应于二进制表示的相反“endian”形式。 “字节序”只是最重要的位是在二进制表示的开头还是结尾。

    【讨论】:

      【解决方案2】:

      是的,你错过了不同的机器支持不同的"endianness"。 Perl 将1 视为'1' 所以(0x31)。因此,您会看到 1 -> 1000(按升序排列)和 3 -> 1100。

      “错误”取决于视角以及您是否向 Perl 提供了足够的信息来了解您想要什么编码和字节序。

      来自pack

      b A bit string (ascending bit order inside each byte, like vec()).
      B A bit string (descending bit order inside each byte).
      

      我想这就是你想要的:

      unpack( 'B*', chr(1))
      

      【讨论】:

      • 您需要超过一个字节才能发挥字节序的作用。
      • 然后,文章又谈到了位的字节序。不过,我从来没有见过它用这种方式。
      【解决方案3】:

      您正在尝试将整数转换为二进制,然后再返回。虽然您可以使用pack 然后unpack 来执行此操作,但更好的方法是将sprintfprintf%b 格式一起使用:

      my $int = 5;
      my $bits = sprintf "%024b\n", $int;
      print "$bits\n";
      

      反之亦然(将 0 和 1 的字符串转换为整数),最好的方法是使用带有 0b 前缀的 oct 函数:

      my $orig = oct("0b$bits");
      print "$orig\n";
      

      正如其他人解释的那样,unpack 需要一个字符串来解包,所以如果你有一个整数,你首先必须将pack 转换成一个字符串。 %b 格式要求以整数开头。

      如果您需要对字节执行大量操作,并且速度至关重要,您可以构建一个查找表:

      my @binary = map { sprintf '%08b', $_ } 0 .. 255;
      
      print $binary[$int];  # Assuming $int is between 0 and 255
      

      【讨论】:

        【解决方案4】:

        ord(1) 是 49。你必须想要像 sprintf("%064b", 1) 这样的东西,尽管这看起来有点过头了。

        【讨论】:

          【解决方案5】:

          您没有具体说明您的期望。我猜你期待00000001

          这是您提供的字节的正确位,至少在非 EBCDIC 系统上是这样。请记住,unpack 的输入是一个字符串(主要是字节串)。也许你想要

          unpack('b*', pack('C', 1))
          

          更新:正如其他人指出的那样,上面给出了10000000。对于00000001,您可以使用

          unpack('B*', pack('C', 1))  # 00000001
          

          【讨论】:

          • 谢谢。看来这就是我要找的。虽然,这是使用嵌套包完成任务的唯一方法。我的意思是也许它非常有效,但如果不是,我只是想知道是否还有其他方法可以解决。我将不得不多次执行此命令。
          • @Rob:是的,还有另一种方法。只需要嵌套包,因为 unpack 适用于字符串,而不是数字;如果你有一个数字,你可能应该只使用 sprintf (见 tchrist 的回答)
          • @Rob、"\x01"chr(1)pack('C', 1) 产生相同的字符串。
          • @Rob:你真的只是问过某事是否是唯一的方法——在Perl中? :)
          • @ikegami:你忘了v1 :)(还有"\cA""\1"use charnames ':full'; "\N{START OF HEADING}"等)
          【解决方案6】:

          你想要“B”而不是“b”。

          $ perl -E'say unpack "b*", "1"'
          10001100
          
          $ perl -E'say unpack "B*", "1"'
          00110001
          

          pack

          【讨论】:

            猜你喜欢
            • 2013-12-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-11-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多