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

时间:2015-06-13 00:05:09

标签: c perl endianness

我正在解析C中的Photoshop原始16位/通道RGB文件,并尝试记录异常数据点。我需要使用16位量子或216 MB Photoshop .RAW文件对多达36个MPix图像进行非常快速的C分析。

< 1%的点有奇怪的肤色,我想用PerlMagick或Perl GD绘制图形,看看它们来自哪里。

C数据文件的前4个字节包含uigned32_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,一个小测试文件的Exact 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%08x\n",
    length($bdat), $xres, $vul, $vul);
Length ($bdat) = 56712, xres=0x6a0b0000, Vax ulong=959919921l=0x39373731

每一个十六进制字符都被破坏了。显然错了Endian,它不是VAX。 "其他"一个是Network Big-endian

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%08x\n", $xres, $nul, $nul);
Xres=0x6a0b0000, NET ulong=825702201l=0x31373739

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

$bits = unpack("b*", vec($bdat, 0, 32));
printf("bits=$bits, len=%d\n", 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%x\n", 
       $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。正确答案中没有&#34; 3&#34;或#34; 1&#34; s。尝试从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 = %u\n", $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=%d\n", $stupid_num, $dec=hex $stupid_num, $dec);
Stupid_num '00000b6a'->0x00000b6a=2922

这就像判断Ugliest Dog比赛一样,但我仍然宁愿维护文本版本而不是更恶劣的C版本。

我知道有很多方法可以在Java / Python / Go / Ruby /.....

中执行此操作

我知道有一些命令行实用程序就是这样做的。

我必须弄清楚我是如何滥用VEC或Unpack,这两种情况都是我使用过的。这是Brain Teasing方面让我疯了! EndianNess == EndianMess !!!

TYVM!

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

鲍罗廷

感谢您寻找&#39;在这。

我的英特尔处理器是小端的。当我把它读回来的时候,它被vec转化为正确的&#34;正确的&#34; big-endian,网络格式。

我刚尝试从BINARY文件中读取它的VERBATIM,它运行正常:

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

如果您尝试打开&#39; V&#39;,$ bdat,那么您会发现它有效

  

这是我的第一次尝试:     $ vul = unpack(&#34; V&#34;,vec($ bdat,0,32)); #UNPACK V!
    printf(&#34;长度(\ $ bdat)=%d,xres = 0x%08x,Vax ulong =%ul = 0x%08x \ n&#34;,         长度($ bdat),$ xres,$ vul,$ vul);     长度($ bdat)= 56712,xres = 0x6a0b0000,Vax ulong = 959919921l = 0x39373731&lt;&lt;&lt;&lt;完全错了!

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

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

我读了[混淆]部分关于&#34;转换为数字与包...&#34;。这就是为什么我的号码落后了。 &gt;&gt;解压缩(&#34; V&#34;,vec($ bdat&#34;&lt;&lt; ... ...}是我错命的尝试,以错误的方式交换来自错误VEC的$ BDAT中的后向数字() - 将FORMAT优先添加到我的架构支持的本机格式。

现在我理解为什么我看到这么多人用字节提取的例子,以避免老大哥的帮助!

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

感谢1E6,

B

1 个答案:

答案 0 :(得分:0)

vecunpack

合并,让您感到困惑

正确的方法就是

unpack 'V', $bdat

按预期返回0x00000B6A的值

vec($bdat, 0, 32)相当于unpack 'N', $bdat,您可以从第一个代码块中$xres的值中看到,documentation for vec通过

确认此内容
  

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

该行

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

是非常错误的,因为vec($bdat, 0, 32)的十进制值是1779105792,因此您在字符串 unpack上调用"1779105792",而不是{{1}}。做任何有用的事情