我正在解析 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
您将vec
与unpack
结合起来混淆了事情
正确的方法很简单
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
,这根本不做任何有用的事情