在 Perl 中解压缩 32 位向量以读取用 C 编写的 uint32 的正确方法



我正在解析 C 格式的 Photoshop 原始 16 位/通道 RGB 文件,并试图保留异常数据点的日志。我需要对多达 36 张 MPix 图像进行非常快速的 C 分析,其中包含 16 位量子或 216 MB Photoshop。原始文件。

C 数据文件的前 4 个字节包含无符号图像宽度作为uint32_t。在 Perl 中,我以二进制模式读取整个文件并提取前 32 位:

Xres=1779105792l = 0x6a0b0000

它看起来很像 C 日志文件:

DA: Color anomalies=14177=0.229%:
DA: II=1) raw PIDX=0x10000b25,  XCols=[0]=0x00000b6a

Dec(0x00000b6a) = 2922,一个小测试文件的确切X_Columns_Width。

显然是英特尔1972 8008 NUXI架构的一个案例。将0x6a0b0000翻译成0x6a0b0000有多难;交换 2 个字节和 2 个半字节,你就完成了。可以切开 8 个字符并重新排列它们,但这是我试图避免的那种丑陋的黑客。

从文件偏移量零中获取相同的 32 位向量,并将其解压缩为"VAX"无符号长整型。

$xres = vec($bdat, 0, 32);  # vec EXPR,OFFSET,BITS
$vul   = unpack("V", vec($bdat, 0, 32));
printf("Length ($bdat)=%d, xres=0x%08x, Vax ulong=%ul=0x%08xn",
    length($bdat), $xres, $vul, $vul);
Length ($bdat) = 56712, xres=0x6a0b0000, Vax ulong=959919921l=0x39373731

每个十六进制字符都被破坏了。显然错误的字节序,它不是VAX。"其他"的是网络大端

http://perldoc.perl.org/functions/pack.html
N  An unsigned long (32-bit) in "network" (big-endian) order.
V  An unsigned long (32-bit) in "VAX" (little-endian) order.
$nul = unpack("N", vec($bdat, 0, 32));  # Network Unsigned Long 32b
printf("Xres=0x%08x, NET ulong=%ul=0x%08xn", $xres, $nul, $nul);
Xres=0x6a0b0000, NET ulong=825702201l=0x31373739

$XRES仍然以错误的顺序显示正确的十六进制。从相同位中提取的"NETWORK"长 32 位 uint 无法识别。尝试二进制

$bits = unpack("b*", vec($bdat, 0, 32));
printf("bits=$bits, len=%dn", length $bits);
   bits=10001100111011001110110010011100100011000000110010101100111011001001110001001100, len=80

我明确要求 32 位并得到 80 位。什么给?

尝试 4 个无符号的 8 位字节,这些字节不能交换:

for($ii = 0; $ii < 4; $ii++)  {
    $bit_off=$ii*8;  # Bit offset
    $uc = unpack("C", vec($bdat, $bit_off, 8));  # C  An unsigned char 
    printf("II $ii, bo $bit_off, d=%d, u=%u, x=0x%xn", 
       $uc,$uc, $uc);
}
II 0, bo 0, d=49, u=49, x=0x31
II 1, bo 8, d=51, u=51, x=0x33
II 2, bo 16, d=49, u=49, x=0x31
II 3, bo 24, d=49, u=49, x=0x31

我正在寻找十六进制 0、6、a 或 b。正确答案中没有"3"或"1"。尝试从 C 文件盗版:

http://cpansearch.perl.org/src/MHX/Convert-Binary-C-0.76/tests/include/include/bits/byteswap.h
$x = $xres;
$x= (((($x) & 0xff000000) >> 24) | ((($x) & 0x00ff0000) >>  8) |     ((($x) & 0x0000ff00) <<  8) | ((($x) & 0x000000ff) << 24));
printf("$xres=0x%08x -> $x=0x%08x = %un", $xres, $x, $x);
$xres=0x6a0b0000 -> $x=0x00000b6a = 2922

它有效!但是,这比将原始的错误顺序十六进制数转换为字符串以解开它更丑陋:

$stupid_str = sprintf("%08x", $xres);
$stupid_num = join('', reverse ($stupid_str =~ m/../g));
printf("Stupid_num '%s'->0x%08x=%dn", $stupid_num, $dec=hex $stupid_num, $dec);
Stupid_num '00000b6a'->0x00000b6a=2922

这就像评判最丑陋的狗比赛一样,但我仍然宁愿保留文本版本,而不是更可恶的 C 版本。

我知道在Java/Python/Go/Ruby/..中有一些方法可以做到这一点。

我知道有一些命令行实用程序可以做到这一点。

必须弄清楚我是如何滥用VEC或Unpack的,这两者都我已经使用了无数次。是大脑戏弄方面让我发疯! EndianNess == EndianMess!!

嗖!

====

====================================================

鲍罗丁,

感谢您查看此内容。

我的英特尔处理器是小端序。当我读回去时,它被vec转换为"正确"的大端网络格式。

我刚刚尝试从读取的二进制文件中逐字读取它,它工作正常:

($b4 = $bdat) =~ s/^(....).*$/$1/msg;   # Give me my 4 bytes back without mutilation!
printf("B4='%s'=>0x%08x=<0x%08xn", $b4, unpack("L>", $b4), unpack("L<", $b4));
B4='j...' = >0x6a0b0000 = <0x00000b6a   <<<  THE RIGHT ANSWER!!!

如果您尝试解压缩"V",$bdat您会发现它可以工作

那是我的第一次尝试: $vul = unpack("V", vec($bdat, 0, 32)); # 解包 V!
printf("Length (\$bdat)=%d, xres=0x%08x, Vax ulong=%ul=0x%08x", 长度($bdat), $xres, $vul, $vul); 长度 ($bdat) = 56712, xres=0x6a0b0000, vax ulong=959919921l=0x39373731 <<<<完全错误!

我已经验证了$BDAT信息是错误格式的正确数据。它只是需要一些重新安排。

我只是使用 vec() 生成 1 位和 4 位图形文件,它忠实地工作,返回我编写的确切位。它一定把我的Intel i7误认为是我的IBM System/370。I7/37???容易犯错误。:)

我读了关于"转换为一个数字,就像包一样......"的[令人困惑的]部分。这就是为什么我的号码落后。>>解包("V", vec($bdat"<<...是我命运多舛的尝试,将$BDAT中的向后数字从错误的 VEC() 首选格式字节交换为我的架构支持的本机格式。

现在我明白为什么我看到那么多人按字节提取的例子,以避免老大哥的帮助了!

Data::BitStream::Vec "uses a Perl vec to store the data. The vector is accessed in 1-bit units"

谢谢1E6,

B

您将vecunpack结合起来混淆了事情

正确的方法很简单

unpack 'V', $bdat

按预期返回 0x00000B6A

vec($bdat, 0, 32)等效于unpack 'N', $bdat,从第一个代码块中的 $xres 值可以看出,vec的文档证实了这一点

如果 BITS 为 16 或更大,则输入字符串的字节被分组为大小为 BITS/8 的块,并且每个组将转换为一个数字,如 pack()/unpack() 的大端格式 n/N

该行

$vul = unpack("V", vec($bdat, 0, 32))

是非常错误的,因为 vec($bdat, 0, 32) 的十进制值是1779105792,所以你然后对字符串"1779105792"调用 unpack,这根本不做任何有用的事情

最新更新