网站建设经典文章,网址生成短链接,中国城乡建中国城乡建设部网站,wordpress连接微信支付最近在看perlpacktut的document#xff0c;整理了下面的文章#xff0c; 希望對大家有幫助#xff0c;瞭解pack/unpack怎麼用... 還沒寫完..後面跟C有關係.. Pack 與unpack使用說明#xff1a; 資料來源摘自perlpacktut#xff0c;初學perl的時候#xff0c;想必大家對於…最近在看perlpacktut的document整理了下面的文章 希望對大家有幫助瞭解pack/unpack怎麼用... 還沒寫完..後面跟C有關係.. Pack 與unpack使用說明 資料來源摘自perlpacktut初學perl的時候想必大家對於pack與unpack都不是很瞭解因此本人擷取perldoc裡面的文章為各位簡單說明pack與unpack的使用方法首先定義一下pack與unpackpack可視為將一系列的片段的數值打包在一起可用於對dev檔案、socket、memory的讀寫因為這些需要一塊完整的memory而且需要事先打包成特定格式而unpack可以視為將將這些完整的 memory切割計算取得我們所需要各部分的Variable。例子如下 print pack(“H2”x10,map{ “3$_” } (0..9); 得到 0123456789 因為ASCII中30~39代表數字0-9。所以pack後可以得到ascii編碼的0-9而我們可以利用unpack將string作逆向操作。 print unpack(“H*”,”0123456789”); 得到 30313233343536373839 同樣的作法可以用在中文字中至是你要注意OS的編碼格式UTF-8、GB2312、Big5得到的數值並不會相同。 Pack與unpack也可以用在對於對於固定格式的文件作處理的情形下 代码: Date |Description | Income|Expenditure 01/24/2001 Ahmeds Camel Emporium 1147.99 01/28/2001 Flea spray 24.99 01/29/2001 Camel rides to tourists 235.00 看到上面格式想必大家很多人都用用substr將固定欄位中的字串取出來或利用有點點複雜的Regular Expression將上面的文件欄位匹配出來而實際上我們要將上面欄位一個一個匹配出來可以用一行搞定 例子 while(){ my($date,$desc,$income,$expend) unpack(“A10xA27xA7xA*”); } 簡單說明A表示ASCIIA10表示10個ASCII characterx表示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就可以讓字串不會都連在一起。 完成程式如下 代码: #!/usr/bin/perl use POSIX; open(FF,tt.txt); while(){ my ($date,$desc,$income,$expend) unpack(A10xA27xA7xA*,$_); $tot_income $income; $tot_expend $expend; $income sprintf(%.2f, $income); $expend sprintf(.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(.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; 整數的pack 對於整數需要注意各個OS所定義的int的長度與各個OS所用的byte order順序是little-endian還是big-endian就是高位元在前還是低位元在前的意思。 例子 my $ps pack(‘s’,20302); s:a signed short intger一般都是16bits2bytes如果我們將他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;才能使用) 代码: 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} 對memory stack作pack 例子如下memory stack長得像下面這樣接著利用unpack將stack中的各個欄位取出。 代码: --------- ---- ---- --------- 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 | ---- ---- --------- 代码: my( $ip, $cs, $flags, $ax, $bx, $cd, $dx, $si, $di, $bp, $ds, $es ) unpack( v12, $frame ); ‘v’ unsigned short in ‘VAX ‘ order 這是取出橫列的IP、CS、FLAGS、AX、BX、CX、DX、SI、DI、BP、DS、ES 代码: my( $fl, $fh, $al, $ah, $bl, $bh, $cl, $ch, $dl, $dh ) unpack( C10, substr( $frame, 4, 10 ) ); ‘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。 代码: ----------------- ----------------- | S Z - A - P - C | - - - - O D I T | ----------------- ----------------- MSB LSB MSB LSB 例子如上圖中「-」表示保留的bit不使用。 將上列這2 bytes 轉換成一個string可以利用’b16’來作 #!usr/bin/perl $status 1101010100001111; $ps pack(b16,$status); print $psn; $statusunpack(b16,$ps); print $statusn; aa split(//,$status); print aan; #---簡化 #---可取得各個bit的數值 ($carry, undef, $parity, undef, $auxcarry, undef, $sign, $trace, $interrupt, $direction, $overflow) split( //, unpack( b16, $status ) ); 如果使用’b12’則後面的4個bit會被忽略並不會被使用。 UuencodeUnix-To-Unix Encode 如果對於UNIX很熟悉的應該知道uuencode與uudecode是幹什麼用的他可視為早期UNIX之間互相傳送資料所用的一種編碼方法將文件編碼以便利於在早期的網路上傳送資料現在很少使用但是有時還是有人會用到編碼原理取出三個bytes將之切割成6份每份4個 bits然後在後面填入x20如此直到整個文件編碼完成。Pack可使用’u’作這件事情。 my $uubuf pack( u, $bindat ); 計算總和(Do Sums) pack中有個特殊的template%專門用來計算總和的但是他不能在pack中使用而且他只能用於其他數字的前置位置prefix。 用於數字時 my $buf pack( iii, 100, 20, 3 ); print unpack( 2i3, $buf ), n; # prints 123 用於字串時 print unpack( 2A*, x01x10 ), n; # prints 17 上面兩個例子%會將後面i3、A*加總起來得到最後結果123與17這可以用來得到最後的check sum。另外不要太相信他所得到的數值因為他們無法保證正確()。 用來取得netmask的bits數目 my $bitcount unpack( 2b*, $mask ); my $evenparity unpack( *, $mask ); 取得evenparity的數值…maskpack(‘b*’,”11111111111111111…0”); Unicode $UTF8{Euro} pack( U, 0x20AC ); 歐洲用字編碼x20AC其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字串加上資料長度再加上資料得到最後要送出的資料。 ZA 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/// - 1; my %env map( split( //, $_ ), unpack( Z* x $n, $env ) ); 而在unpack時要先算出$env裡面到底有多少個$element在裡面這可以透過tr來計算。