`
jedy
  • 浏览: 147775 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

pack/unpack用法

阅读更多
pack/unpack用法----心得筆記
http://www.chinaunix.net 作者:apile 发表于:2003-10-03 19:00:21
最近在看perlpacktut的document,整理了下面的文章,
希望對大家有幫助,瞭解pack/unpack怎麼用...
還沒寫完..後面跟C有關係..
======================================
Pack 與unpack使用說明:
資料來源摘自perlpacktut,初學perl的時候,想必大家對於pack與unpack都不是很瞭解,因此本人擷取perldoc裡面的文章,為 各位簡單說明pack與unpack的使用方法:首先定義一下pack與unpack:pack可視為將一系列的片段的數值打包在一起,可用於對dev檔 案、socket、memory的讀寫,因為這些需要一塊完整的memory,而且需要事先打包成特定格式;而unpack可以視為將將這些完整的 memory切割計算,取得我們所需要各部分的Variable。例子如下:
print pack("H2"x10,map{ "3$_" } (0..9);
print pack("H14",30313233343536);#这里的3?都是16进制的ASC
得到
0123456789
因為ASCII中30~39代表數字0-9。所以pack後可以得到ascii編碼的0->9,而我們可以利用unpack將string作逆向操作。
print unpack("H*","0123456789");
得到
30313233343536373839
同樣的作法可以用在中文字中,至是你要注意OS的編碼格式,UTF-8、GB2312、Big5得到的數值並不會相同。

Pack與unpack也可以用在對於對於固定格式的文件作處理的情形下:
[code:1:c98317aa26]
Date |Description | Income|Expenditure
01/24/2001 Ahmed's Camel Emporium 1147.99
01/28/2001 Flea spray 24.99
01/29/2001 Camel rides to tourists 235.00
[/code:1:c98317aa26]

看到上面格式,想必大家很多人都用用substr將固定欄位中的字串取出來,或利用有點點複雜的Regular Expression將上面的文件欄位匹配出來,而實際上我們要將上面欄位一個一個匹配出來可以用一行搞定
例子:
while(<ff>{
my($date,$desc,$income,$expend) = unpack("A10xA27xA7xA*");
}
簡單說明:A表示ASCII,A10表示10個ASCII character,x表示null byte也等於skip a byte,也就是說我們要跳過一個char(|),然後接著27個ASCII char,然後跳過一個vhar,再接上7個ASCII,在跳過一個char,最後A*表示不管後面char有多少個,我全含括進來。

這樣子就可以得到各個欄位相對應的資料,很簡單吧! 不需要什麼太多的技巧,一行搞定。之後將income與expend作加總,得到total_income、total_expend,數值,然後再輸出 的時候,如果也要格式對稱,要怎麼辦?大多數人都會用format或sprintf,然後一行一行很辛苦的,將結果輸出。其實可以簡化成:
$income = sprintf("%.2f", $income); # Get them into
$expend = sprintf("%.2f", $expend); # "financial" format
將$income與$expend先格式化,然後同上例將結果輸出:
print pack("A11 A28 A8 A*", $date, "Totals", $income, $expend);
注意:這邊多加一個char主要是因為'x'表示的是null byte,所以如果用A7xA27xA7xA*,則輸出的字串會連在一起,而多給他一個char,就可以讓字串不會都連在一起。
完成程式如下:
[code:1:c98317aa26]
#!/usr/bin/perl
use POSIX;
open(FF,"tt.txt");
while(<ff>){
my ($date,$desc,$income,$expend) = unpack("A10xA27xA7xA*",$_);
$tot_income += $income;
$tot_expend += $expend;
$income = sprintf("%.2f", $income);
$expend = sprintf("%12.2f", $expend);
print pack("A11 A28 A8 A*", $date, "$desc", $income, $expend),"\n";
}
$tot_income = sprintf("%.2f", $tot_income); # Get them into
$tot_expend = sprintf("%12.2f", $tot_expend); # "financial" format
$date = POSIX::strftime("%m/%d/%Y", localtime);
print pack("A11 A28 A8 A*", $date, "Totals", $tot_income, $tot_expend),"\n";
[/code:1:c98317aa26]

整數的pack:
對於整數需要注意各個OS所定義的int的長度,與各個OS所用的byte order順序是little-endian還是big-endian,就是高位元在前還是低位元在前的意思。
例子:
my $ps = pack('s',20302);
s:a signed short intger,一般都是16bits=2bytes,如果我們將他print出來(列印這些pack後的字元,實際上是不具任何意義的),會發現他等於NO或ON,然後將$ps在unpack可以得到20302
unpack('s','NO'); ----------------->20302

注意:如果今天你用"s",pack一個大於65535的數字,高位元會自動被刪除,而得到與你想要的數字不同的結果,這點需要注意。

"l": signed 32bits integer
"L":unsigned 32bits integer
"q":signed 64bits intger
"Q":unsigned 64bits integer
"i":signed integers of "local custom" variety
"I":unsigned integers of "local custom" variety
這兩個"i"與"I"主要與機器的OS定義有關係,其長度等於c中的sizeof(int),如果要用於perl與其他語言的溝通,最好使用這個編碼原則。
對照表:(其中%Config要先use Config;才能使用)
[code:1:c98317aa26]
signed unsigned byte length in C byte length in Perl
s! S! sizeof(short) $Config{shortsize}
i! I! sizeof(int) $Config{intsize}
l! L! sizeof(long) $Config{longsize}
q! Q! sizeof(longlong) $Config{longlongsize}
[/code:1:c98317aa26]
對memory stack作pack:
例子如下:memory stack長得像下面這樣,接著利用unpack將stack中的各個欄位取出。
[code:1:c98317aa26]
+---------+ +----+----+ +---------+
TOS: | IP |TOS+4: | FL | FH | FLAGS TOS+14:| SI |
+---------+ +----+----+ +---------+
| CS | | AL | AH | AX | DI |
+---------+ +----+----+ +---------+
| BL | BH | BX | BP |
+----+----+ +---------+
| CL | CH | CX | DS |
+----+----+ +---------+
| DL | DH | DX | ES |
+----+----+ +---------+
[/code:1:c98317aa26]
[code:1:c98317aa26]
my( $ip, $cs, $flags, $ax, $bx, $cd, $dx, $si, $di, $bp, $ds, $es ) = unpack( 'v12', $frame );
[/code:1:c98317aa26]
'v' :unsigned short in 'VAX ' order
這是取出橫列的IP、CS、FLAGS、AX、BX、CX、DX、SI、DI、BP、DS、ES
[code:1:c98317aa26]
my( $fl, $fh, $al, $ah, $bl, $bh, $cl, $ch, $dl, $dh ) =
unpack( 'C10', substr( $frame, 4, 10 ) );
[/code:1:c98317aa26]

'C':unsign character value,用於bytes讀取
這樣子分兩行很麻煩,所以將他們放在一起得到:
my( $ip, $cs,
$flags,$fl,$fh,
$ax,$al,$ah, $bx,$bl,$bh, $cx,$cl,$ch, $dx,$dl,$dh,
$si, $di, $bp, $ds, $es =
unpack( 'v2' . ('vXXCC' x 5) . 'v5', $frame ;
'X':Backup a byte

網路應用:
'n': An unsigned short in "network" (big-endian) order
'N': An unsigned long in "network" (big-endian) order
在作網路連結時,往往需要將長度先送給Server,使其知道後面有多少character要讀取,因此如果有一段msg,可以利用下列方式打包:
my $buf = pack( 'N', length( $msg . $msg;
說明:等於「長度」( unsigned long)加上訊息內容,也可簡化為:
my $buf = pack('NA*',length($msg),$msg);
然後將$buf送至對方server。同樣對方可用unpack('NA*',$buf)取得送出的數據。

Floating Point Numbers:
'f': float (A single-precision float in the native format)
'd':double(A double-precision float in the native format)
對於浮點數(float point)可以使用f或d來作pack與unpack動作。

奇特的例子:
Bit Strings:
String中都是0或1,對其作pack/unpack的轉換,需要注意那一系列的0/1,與每八個bit一個字元的順序性。假設今天有個string等於 "10001100"可以用下列方式:
$byte = pack( 'B8', '10001100' ; # start with MSB
$byte = pack( 'b8', '00110001' ; # start with LSB

b A bit string (ascending bit order inside each byte, like vec()).(漸增)
B A bit string (descending bit order inside each byte).(漸減)
如果要對string中的一部份bits作pack/unpack是不可能的,因為pack會從最旁邊開始,然後以8個bits為一組,如果不足八個則補0。
[code:1:c98317aa26]
+-----------------+-----------------+
| S Z - A - P - C | - - - - O D I T |
+-----------------+-----------------+
MSB LSB MSB LSB
[/code:1:c98317aa26]
例子:如上圖中「-」表示保留的bit,不使用。

將上列這2 bytes 轉換成一個string,可以利用'b16'來作:
#!usr/bin/perl
$status = "1101010100001111";
$ps= pack('b16',$status);
print "$ps\n";
$status=unpack('b16',$ps);
print "$status\n";
@aa = split(//,$status);
print "@aa\n";
#---簡化
#---可取得各個bit的數值
($carry, undef, $parity, undef, $auxcarry, undef, $sign,
$trace, $interrupt, $direction, $overflow) =
split( //, unpack( 'b16', $status ;

如果使用'b12'則,後面的4個bit會被忽略並不會被使用。

Uuencode:(Unix-To-Unix Encode)
如果對於UNIX很熟悉的,應該知道uuencode與uudecode是幹什麼用的,他可視為早期UNIX之間互相傳送資料,所用的一種編碼方法,將文 件編碼,以便利於在早期的網路上傳送資料,現在很少使用,但是有時還是有人會用到,編碼原理:取出三個bytes,將之切割成6份,每份4個bits,然 後在後面填入\x20,如此直到整個文件編碼完成。Pack可使用'u'作這件事情。

my $uubuf = pack( 'u', $bindat ;

計算總和:(Do Sums)
pack中有個特殊的template,%<number>專門用來計算總和的,但是他不能在pack中使用,而且他只能用於其他數字的前置位置(prefix)。

用於數字時:
my $buf = pack( 'iii', 100, 20, 3 ;
print unpack( '%32i3', $buf , "\n"; # prints 123
用於字串時:
print unpack( '%32A*', "\x01\x10" , "\n"; # prints 17
上面兩個例子,%會將後面i3、A*加總起來,得到最後結果123與17,這可以用來得到最後的check sum。另外不要太相信他所得到的數值,因為他們無法保證正確(?)。
用來取得netmask的bits數目:
my $bitcount = unpack( '%32b*', $mask ;


my $evenparity = unpack( '%1b*', $mask ;
取得evenparity的數值…mask=pack('b*',"11111111111111111…0");

Unicode:
$UTF8{Euro} = pack( 'U', 0x20AC ;


歐洲用字編碼\0x20AC,其UTF8的編碼字元為$URF{Euro}。
# pack and unpack the Hebrew alphabet
my $alefbet = pack( 'U*', 0x05d0..0x05ea ;
my @hebrew = unpack( 'U*', $utf ;
用於pack/unpack unicode編碼

另一種Portable編碼:
w A BER compressed integer. Its bytes represent an unsigned integer in base 128, most significant digit first, with as few digits as possible. Bit eight (the high bit) is set on each byte except the last.

my $berbuf = pack( 'w*', 1, 128, 128+1, 128*128+127 ;


長度與寬度(Length and Width)
字串長度(String Length)
在傳送網路資料時,往往需要將資料的長度放在封包的標頭處,讓對方Server知道後續還有多少資料需要處理:下列例子為兩個null terminated字串加上資料長度,再加上資料,得到最後要送出的資料。
Z:A null terminated (ASCIZ) string, will be null padded.
my $msg = pack( 'Z*Z*CA*', $src, $dst, length( $sm , $sm ;

而要取出資料可以用下列方式:
( $src, $dst, $len, $sm = unpack( 'Z*Z*CA*', $msg ;


但是,如果我們在pack後面加上另一個C,則在unpack時,會無法正確得到資料。因為A*會把所有的剩餘character都抓給$sm,而使得$prio變成無定義。
# pack a message
my $msg = pack( 'Z*Z*CA*C', $src, $dst, length( $sm , $sm, $prio ;

# unpack fails - $prio remains undefined!
( $src, $dst, $len, $sm, $prio = unpack(


因此可以使用下列方式取得資料:
# pack a message: ASCIIZ, ASCIIZ, length/string, byte
my $msg = pack( 'Z* Z* C/A* C', $src, $dst, $sm, $prio ;

# unpack
( $src, $dst, $sm, $prio = unpack( 'Z* Z* C/A* C', $msg ;

加上"/"符號,可以使得perl在pack A*會記住$sm的長度,然後後續的$prio就可以區別出來儲存,而在unpack時,就可以依照$sm的長度,而將$sm的資料取出,剩下來的就是$prio的資料。"/"只有在後面接上A*、a*、Z*時有意義。
"/"表示後面A*的長度,佔1個byte
# pack a message: ASCIIZ, ASCIIZ, length/string, byte
my $msg = pack( 'Z* Z* C/A* C', $src, $dst, $sm, $prio ;

# unpack
( $src, $dst, $sm, $prio = unpack( 'Z* Z* C/A* C', $msg ;

"/"可以代表任何數字,用以表示其後A*的長度
# pack/unpack a string preceded by its length in ASCII
my $buf = pack( 'A4/A*', "Humpty-Dumpty" ;
# unpack $buf: '13 Humpty-Dumpty'
my $txt = unpack( 'A4/A*', $buf ;

"/"在perl 5.6以後才出現,之前版本並不支援。針對舊版本需要做如下修改已取得長度。
# pack a message: ASCIIZ, ASCIIZ, length, string, byte (5.005 compatible)
my $msg = pack( 'Z* Z* C A* C', $src, $dst, length $sm, $sm, $prio ;

# unpack
( undef, undef, $len) = unpack( 'Z* Z* C', $msg ;#--先取得長度
#--依照長度設定A的長度
($src, $dst, $sm, $prio) = unpack ( "Z* Z* x A$len C", $msg ;


Dynamic Template
從前面到現在我們看到的都是有固定長度的範例,但是如果碰到沒有固定長度的怎麼辦?以下有個例子來說明:
my $env = pack( 'A*A*Z*' x keys( %Env . 'C',
map( { ( $_, '=', $Env{$_} } keys( %Env , 0 ;

為了方便讓C來Parsing這個string,所以利用兩個string與一個null terminated字串,利用map將$ENV中的參數讀出來,然後利用(AAA,=,BBB)的方式,儲存成一個array,然後傳給pack,然後 pack利用"A*A*Z*將AAA,=,BBB給打包起來,keys(%ENV)表示ENV hash總共有多少個$element在裡面。最後為了方便C parsing,在最後面加上一個"0"。
my $n = $env =~ tr/\0// - 1;
my %env = map( split( /=/, $_ , unpack( 'Z*' x $n, $env ;

而在unpack時,要先算出$env裡面到底有多少個$element在裡面,這可以透過tr來計算。
分享到:
评论

相关推荐

    sap-me-pack-unpack-how-to-guide-en.pdf

    ### SAP ME Pack and Unpack功能设置与使用指南 #### 概述 本指南旨在为用户提供关于如何在SAP Manufacturing Execution (SAP ME)系统中设置和使用Pack与Unpack功能的详细步骤。此功能主要用于处理制造过程中产品...

    jdk1.6.0_191.linux.x64.tar.gz

    解压后使用方法:(不执行下面两步,运行java -version会报下面的错误,解压命令:tar xvzf jdk1.6.0_191.linux.x64.tar.gz) # Error occurred during initialization of VM # java/lang/NoClassDefFoundError: java...

    java模拟PHP的pack和unpack类_.docx

    `PackUtil`类包含两个主要方法:`pack`和`unpack`。 1. `pack`方法: 这个方法用于将一个字符串打包成字节数组。它的工作原理是遍历输入字符串的每个字符,将字符转换为其16进制值,并将其存储到字节数组中。这里的...

    java模拟PHP的pack和unpack类

    通过以上代码,Java程序员现在可以使用`PackUtil.pack()`将16进制字符串转换为字节数组,然后使用`PackUtil.unpack()`将字节数组恢复为16进制字符串。这种方法虽然不能完全模拟PHP的`pack`和`unpack`函数的所有功能...

    解析二进制流接口应用实例 pack、unpack、ord 函数使用方法

    在工作中,我也逐渐了解到pack,unpack,ord对于二进制字节处理的强大。 下面我逐一介绍它们。在我们工作中,用到它们的估计不多。 我在最近一个工作中,因为通讯需要用到二进制流,然后接口用php接收。当时在处理时候...

    Python使用struct处理二进制(pack和unpack用法)

    读取文件内容后,可以使用`struct.unpack`解码,写入文件则使用`struct.pack`编码。 总之,Python的`struct`模块提供了强大的工具,用于在Python对象和二进制数据之间进行转换,这对于处理底层数据结构或与系统底层...

    pack_unpack_file

    这里我们主要探讨的是使用C++语言进行文件打包(压缩)和解包(解压)的方法。"pack_unpack_file"可能是一个项目或者库,专门用于处理这类任务。在C++中,虽然没有内置的压缩和解压库,但我们可以通过第三方库如Zlib...

    浅谈PHP中pack、unpack的详细用法

    PHP中有两个函数pack和unpack,很多PHPer在实际项目中从来没有使用过,甚至也不知道这两个方法是用来干嘛的。这篇文章来为大家介绍一下它俩到底是用来干啥的。 pack string pack ( string $format [, mixed $args ...

    php pack与unpack 摸板字符字符含义

    理解这些模板字符对于有效使用 `pack` 和 `unpack` 功能至关重要,因为它们可以帮助你在各种场景中高效地处理二进制数据。通过熟练掌握这些字符及其用法,你可以轻松地将 PHP 数据结构转换为二进制流,或者从网络...

    HOWTO Unpack, Edit, and Re-Pack Boot Images

    如果不熟悉这种方法,可以使用作者提供的解包脚本,它会将img文件解包到当前目录,方便编辑。重新打包时,可以使用find命令和cpio及gzip工具将修改后的文件打包成新的img文件。最后,需要用到mkbootimg工具将内核和...

    xtcp:具有正常关闭,自定义协议的TCP Server框架

    xtcp 具有正常关闭,自定义协议的TCP Server...// Protocol use to pack/unpack Packet. type Protocol interface { // return the size need for pack the Packet. PackSize ( p Packet ) int // PackTo pack th

    pack200-maven-plugin:Maven 插件来执行“pack200 - JAR 打包工具”

    pack200-maven-插件 用于 Travis CI 传递使用 maven 项目中的 pack200 和 unpack200 命令的插件。用法将以下插件添加到您的 pom.xml 文件中: &lt;groupId&gt;...

    7zip-min:适用于Node.js的带有7-zip的最小独立跨平台packunpack

    用法您可以使用pack unpack方法进行简单打包。 您还可以使用list获取具有文件内容属性(包括日期,时间,属性,大小,压缩和名称)的数组。 或使用cmd通过自定义参数运行7za(请参阅《 ) const _7z = require ( '7...

    pack.cr:来自Perl Ruby的Crystal编译时(un)pack宏

    在实际使用中,`pack.cr`库的用法可能如下: ```crystal require "pack" data = [127, 3.14, "Hello".to_slice] packed = Pack.pack("Ci*a", data) # 对packed进行网络传输或写入文件... unpacked_data = Pack....

    PHP处理二进制数据的实现方法

    pack()用来将数据转成二进制数据,使用方法如下: pack(“LL”, 0,1); pack(“C”, a); unpack()可以将二进制数据解析成关系数组,它接受2个参数,使用方法如下: $arr = unpack(“Chead”, $binstream); //读取第1...

    venv-pack:打包用于重新分发的虚拟环境

    下面我们将详细探讨`venv-pack`的工作原理、使用方法以及其在Python开发中的重要性。 `venv-pack`的核心功能在于将一个已创建的虚拟环境转换为一组文件和目录,这些文件可以被压缩并分发给其他人。这解决了在部署或...

    lichee_20170502_1607_全志R16的linux系统编译需要改动的文件_使用parrotv1.1的内核_没有外层目录.7z

    rootroot@cm-System-Product-Name:/home/wwt/linux_r16$ tar zxvf lichee_parrotv1.1_20161202.tar.gz rootroot@cm-System-Product-Name:/home/wwt/linux_r16$ ...Preparing to unpack .../libencode-locale-perl_...

Global site tag (gtag.js) - Google Analytics