簡體   English   中英

在 Perl 中解壓 32 位向量以讀取用 C 編寫的 uint32 的正確方法

[英]Correct way to unpack a 32 bit vector in Perl to read a uint32 written in C

我正在用 C 解析 Photoshop 原始、16 位/通道、RGB 文件,並試圖保留異常數據點的日志。 我需要對多達 36 個 MPix 圖像和 16 位量子或 216 MB Photoshop .RAW 文件進行非常快速的 C 分析。

<1% 的點具有奇怪的膚色,我想用 PerlMagick 或 Perl GD 繪制它們以查看它們的來源。

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,一個小測試文件的 Exact X_Columns_Width。

顯然是英特爾 1972 年 8008 NUXI 架構的一個案例。 將 0x6a0b0000 轉換為 0x6a0b0000 可能有多難; 交換 2 個字節和 2 個半字節就完成了。 可以將 8 個字符切片並重新排列它們,但這是我試圖避免的那種丑陋的 hack。

從文件偏移量零獲取相同的 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。 “另一個”是網絡大端

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。 正確答案中沒有“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 = %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

就像在評判最丑狗大賽,但我還是寧願維護文本版本,也不願維護更可惡的C版本。

我知道在 Java/Python/Go/Ruby/.....

我知道有一些命令行實用程序可以做到這一點。

我必須弄清楚我是如何誤用 VEC 或 Unpack 的,我已經使用了無數次。 這是讓我抓狂的腦筋急轉彎方面! EndianNess == EndianMess!!!

TYVM!

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

鮑羅丁,

謝謝你看這個。

我的英特爾處理器是小端的。 當我讀回它時,它被 vec 轉換為“正確的”大端網絡格式。

我只是嘗試從讀取的二進制文件中逐字讀取它,它工作正常:

($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!!!

如果你嘗試解壓 'V', $bdat 那么你會發現它有效

那是我的第一次嘗試: $vul = unpack("V", vec($bdat, 0, 32)); # 解壓 V!
printf("長度(\\$bdat)=%d, xres=0x%08x, Vax ulong=%ul=0x%08x\\n", length($bdat), $xres, $vul, $vul); 長度($bdat)= 56712,xres=0x6a0b0000,Vax ulong=959919921l=0x39373731 <<<< 完全錯誤!

我已經確認 $BDAT 信息是格式錯誤的正確數據。 它只需要一些重新排列。

我只是使用 vec() 生成 1 位和 4 位圖形文件,它忠實地工作,返回我寫的確切位。 它一定把我的 Intel i7 誤認為是我的 IBM System/370。 I7/37??? 容易犯錯。 :)

我閱讀了[令人困惑的] 部分關於“像包一樣轉換為數字......”。 這就是為什么我的號碼落后的原因。 >>unpack("V", vec($bdat"<< ... 是我將 $BDAT 中的后向數字從 WRONG 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 ,它根本沒有做任何有用的事情

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM