say pack "A*", "asdf"; # Prints "asdf"
say pack "s", 0x41 * 256 + 0x42; # Prints "BA" (0x41 = 'A', 0x42 = 'B')
第一行是有道理的:您正在使用ASCII编码的字符串,将其作为ASCII字符串打包到字符串中。在第二行中,打包的形式为“\ x42 \ x41”,因为我机器上的短整数有小端。
然而,我不能动摇这种感觉,不知怎的,我应该能够将第二行的打包字符串视为一个数字,因为这就是(我假设)Perl存储数字的方式,因为小端字节序列。有没有办法在不拆包的情况下这样做?我正在尝试为pack()返回的东西获得正确的心理模型。
例如,在C中,我可以这样做:
#include <stdio.h>
int main(void) {
char c[2];
short * x = c;
c[0] = 0x42;
c[1] = 0x41;
printf("%d\n", *x); // Prints 16706 == 0x41 * 256 + 0x42
return 0;
}
答案 0 :(得分:4)
如果您真的对Perl如何在内部存储数据感兴趣,我建议PerlGuts Illustrated。但通常情况下,您不必关心类似的东西,因为Perl不会让您访问这些低级别的细节。只有在C语言中编写XS扩展时,这些内部结构才是重要的。
如果要将两个字节的字符串“强制转换”为C short
,可以像这样使用unpack
函数:
$ perl -le 'print unpack("s", "BA")'
16706
答案 1 :(得分:3)
然而,我无法摆脱这种感觉,不知何故,我应该能够将第二行的打包字符串视为一个数字,
您需要先解压缩它。
为了能够将其用作C中的数字,您需要
char* packed = "\x42\x41";
int16_t int16;
memcpy(&int16, packed, sizeof(int16_t));
为了能够在Perl中将其用作数字,您需要
my $packed = "\x42\x41";
my $num = unpack('s', $packed);
基本上是
use Inline C => <<'__EOI__';
SV* unpack_s(SV* sv) {
STRLEN len;
char* buf;
int16_t int16;
SvGETMAGIC(sv);
buf = SvPVbyte(sv, len);
if (len != sizeof(int16_t))
croak("usage");
Copy(buf, &int16, 1, int16_t);
return newSViv(int16);
}
__EOI__
my $packed = "\x42\x41";
my $num = unpack_s($packed);
因为这是(我假设)perl存储数字的方式,作为little-endian字节序列。
Perl将数字存储在标量的以下三个字段之一中:
IV
,大小为perl -V:ivsize
的有符号整数(以字节为单位)。UV
,一个大小为perl -V:uvsize
的无符号整数(以字节为单位)。 (ivsize = uvsize)NV
,大小为perl -V:nvsize
的浮点数(以字节为单位)。在所有情况下,都使用本机字节序。
我正在尝试为pack()返回的东西获取正确的心理模型。
pack
用于构建“二进制数据”,以便与外部API连接。
答案 2 :(得分:2)
我将pack
视为序列化函数。它将Perl值作为输入,并输出序列化形式。输出序列化形式恰好是Perl字节串的事实更多的是实现细节而不是核心功能。
因此,你真正期望用结果字符串做的就是将它解压缩,尽管序列化表格很方便它可以在进程,主机和行星周围移动。
如果您有兴趣将其序列化为数字,请考虑使用vec
:
say vec "BA", 0, 16; # prints 16961
要仔细查看字符串的内部表示,请查看Devel::Peek,尽管您不会看到任何令人惊讶的纯ASCII字符串。
use Devel::Peek;
Dump "BA";
SV = PV(0xb42f80) at 0xb56300
REFCNT = 1
FLAGS = (POK,READONLY,pPOK)
PV = 0xb60cc0 "BA"\0
CUR = 2
LEN = 16