日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

pack()函數(shù)

 Ralf_Jones 2006-08-29

在看下面一個例子前先提及一組函數(shù)pack,unpack。
任何一款擁有socket操作能力的語言都有一個專門用于組包的函數(shù),php也不例外當(dāng)然這組函數(shù)的用途不僅僅是組包。
下面簡單的介紹一下:
應(yīng)用一:
輸入16進制或者2進制流。

<?php
$src="3B06";
$binvar = pack(H*,$src);
echo $binvar;
?>

看看這個程序,相當(dāng)于下面的程序

echo chr(0x3B).chr(0x06);

在數(shù)據(jù)量很小的時候后面的做法,更為簡便。但是大量數(shù)據(jù)的時候,前一種做法則更為實際工整些,代碼量也很少。

例:

@pairs=split(/&/,$query);
foreach $pairs (@pairs) {
 ($name,$value)=split(/=/,$pairs);
 $value=~tr/+/ /;
 $value=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
 $FORM{$name}=$value;
}
 
解釋:
pack TEMPLATE, LIST

這個函數(shù)接收一個普通 Perl 數(shù)值的 LIST 并且根據(jù) TEMPLATE 把它們轉(zhuǎn)換成一個字節(jié)串并且返回該字串。參數(shù)列表在必要時會被填充或者截除。也就是說,如果你提供的參數(shù)比 TEMPLATE 要求的少,pack 假設(shè)缺的都是空值。如果你提供的參數(shù)比 TEMPLATE 要求的多,那么多余的參數(shù)被忽略。在 TEMPLATE 里無法識別的格式元素將會拋出一個例外。

這個模板把該字串的結(jié)構(gòu)描述成一個域序列。每個域都由一個字符代表,描述該值的類型和其編碼。比如,一個格式字符 N 聲明一個四個字節(jié)的無符號整數(shù),字節(jié)序是大頭在前。

域是以模板中給出的順序包裝的。比如,如果要把一個一字節(jié)的無符號整數(shù)和一個單精度浮點數(shù)值包裝到一個字串里,你要說:

   $string = pack("CF", 244, 3.14);

返回的字串的第一個字節(jié)的值是 244。剩下的字節(jié)是 3.14 作為單精度浮點數(shù)的編碼。浮點數(shù)的具體編碼方式取決于你的計算機的硬件。

有些包裝時要考慮的重要事情是:

  • 數(shù)據(jù)的類型(比如是整數(shù)還是浮點還是字串),
  • 數(shù)值的范圍(比如你的整數(shù)是否放在一個,兩個,四個,或者甚至八個字節(jié)里;或者你包裝的是一個 8 位字符還是 Unicode 字符。),
  • 你的整數(shù)是有符號還是無符號,以及
  • 使用的編碼(比如說本機,包裝位和字節(jié)時小頭在前,或者是大頭在前)。

表 29-3 列出了格式字符以及它們的含義。(其他字符也可能在格式中出現(xiàn);它們在稍后介紹。)

表29-3,pack/unpack 的摸板字符

字符 含義
a 一個填充空的字節(jié)串
A 一個填充空格的字節(jié)串
b 一個位串,在每個字節(jié)里位的順序都是升序
B 一個位串,在每個字節(jié)里位的順序都是降序
c 一個有符號 char(8位整數(shù))值
C 一個無符號 char(8位整數(shù))值;關(guān)于 Unicode 參閱 U
d 本機格式的雙精度浮點數(shù)
f 本機格式的單精度浮點數(shù)
h 一個十六進制串,低四位在前
H 一個十六進制串,高四位在前
i 一個有符號整數(shù)值,本機格式
I 一個無符號整數(shù)值,本機格式
l 一個有符號長整形,總是 32 位
L 一個無符號長整形,總是 32 位
n 一個 16位短整形,“網(wǎng)絡(luò)”字節(jié)序(大頭在前)
N 一個 32 位短整形,“網(wǎng)絡(luò)”字節(jié)序(大頭在前)
p 一個指向空結(jié)尾的字串的指針
P 一個指向定長字串的指針
q 一個有符號四倍(64位整數(shù))值
Q 一個無符號四倍(64位整數(shù))值
s 一個有符號短整數(shù)值,總是 16 位
S 一個無符號短整數(shù)值,總是 16 位
u 一個無編碼的字串
U 一個 Unicode 字符數(shù)字
v 一個“VAX”字節(jié)序(小頭在前)的 16 位短整數(shù)
V 一個“VAX”字節(jié)序(小頭在前)的 32 位短整數(shù)
w 一個 BER 壓縮的整數(shù)
x 一個空字節(jié)(向前忽略一個字節(jié))
X 備份一個字節(jié)
Z 一個空結(jié)束的(和空填充的)字節(jié)串
@ 用空字節(jié)填充絕對位置

你可以在你的 TEMPLATE 里自由地使用空白和注釋。注釋以慣用的 # 符號開頭并且延伸到 TEMPLATE 里第一個換行符(如果存在)。

每個字母后面都可以跟著一個數(shù)字,表示 count(計數(shù)),解釋成某種形式的重復(fù)計數(shù)或者長度,具體情況取決于格式。除了a,A,b,B,h,H,P,和 Z 之外,所有格式的 count 都是重復(fù)次數(shù),因此 pack 從 LIST 里吃進那么多數(shù)值。如果 count 是一個 * 表示剩下的所有東西。

a,A 和 Z 格式只吃進一個數(shù)值,但是把它當(dāng)作一個長度為 count 的字節(jié)串打包,并根據(jù)需要填充空或者空格。在解包的時候,A 抽去結(jié)尾的空格和空,Z 抽去第一個空后面的所有東西,而 a 原封不動地返回文本數(shù)據(jù)。打包的時候,a 和 Z 是相同的。

與之類似,b 和 B 格式打包一個長度為 count 的位串。輸入域里的每個字節(jié)都以每個輸入字節(jié)的最低位(也就是 ord($byte) % 2)為基礎(chǔ)生成結(jié)果中的 1 個位。方便一點來說,這意味著字節(jié) 0 和 1 生成位 0 和 1。從輸入字串的開頭開始,每 8 個字節(jié)轉(zhuǎn)換成一個字節(jié)的輸出。如果輸入字串的長度不能被 8 整除,那么余下的部分用 0 補齊。類似,在 uppack 的時候,任何額外的位都被忽略。如果輸入字串比需要的長,那么多出來的部分被忽略。count 如果是 * 意思是使用輸入域的所有字節(jié)。在解包的時候,這些位轉(zhuǎn)換成一個 0 和 1 組成的字串。

h 和 H 格式打包一個由 count 個半字節(jié)(4 位組,常用于代表十六進制位。)組成的字串。

p 格式打包一個指向一個空結(jié)尾的字串。你有責(zé)任確保該字串不是一個臨時數(shù)值(因為臨時值可能在你能使用打包的結(jié)果之前很可能被釋放掉)。P 格式打包一個指向一個結(jié)構(gòu)的指針,該結(jié)構(gòu)的大小由 count 指明。如果對應(yīng)的 p 或 P 的值是 undef,則創(chuàng)建一個空指針。

/ 字符允許對這樣一個字串進行打包或解包:這個打了包的結(jié)構(gòu)包含一個字節(jié)數(shù)以及后面跟著字串本身。你可以這樣寫 length-item/string-item。length-item 可以是任意 pack 模板字符,并且描述長度值是如何打包的。最常用的可能是那些整數(shù)打包的東西,比如 n(用于打包 Java 字串),w(用于打包 ASN.1 或 SNMP)以及 N(用于 Sun XDR)。string-item 目前必須是 A*,a*,或者 Z*。對于 unpack 而言,字串的長度是從 length-item 里獲取的,但是如果你放 * 進去,那么它將被忽略。

   unpack ‘C/a‘,   "\04Gurusamy";      # 生成 ‘Guru‘
uppack ‘a(chǎn)3/A* A*‘, ‘077 Bond J ‘;      # 生成 (‘ Bond‘, ‘J‘)
pack ‘n/a* w/a*‘, ‘hell‘, ‘,‘world‘;      #  生成 "hello, world"

length-item 不會從 unpack 明確地返回。向 length-item 字母加一個 count 不一定能干有用的事,除非該字母是 A,a,或 Z。用帶 length-item 的 a 或 Z 進行打包可能會引入空(\0)字符,這時候,Perl 在會認為它不是合法數(shù)字字串。

整數(shù)格式 s,S,l,和 L 的后面可以緊跟一個 !,表示本機的短整數(shù)或者長整數(shù),而不是各自準(zhǔn)確的 16 位和 32 位?,F(xiàn)在,這是一個在許多 64 位平臺上的問題,在這些平臺上本地 C 編譯器看到本機短整數(shù)和長整數(shù)可能和上面的這些值不同。(i! 和 I! 也可以用,不過只不過是為了保持完整;它們與 i 和 I 相同。)

你可以通過 Config 模塊獲取制作你的 Perl 的平臺上的本機的 short,int,long 和 long long 的實際長度:

   use Config;
print $Config{shortsize},      "\n";
print $Config{intsize},      "\n";
print $Config{longsize},      "\n";
print $Config{longlongsize},   "\n";

這里只是說 Configure 知道一個 long long 的大小,但著并不意味著你就能用 q 和 Q。(有些系統(tǒng)能有,不過你用的系統(tǒng)很可能還沒有。)

長度大于一個字節(jié)的整數(shù)格式(s,S,i,I,l,和 L)都是天生不能在不同處理器之間移植的,因為它們要遵從本機字節(jié)序和位權(quán)重序的規(guī)則。如果你需要可移植的整數(shù),那么使用格式 n,N, v,和 V;因為它們是字節(jié)權(quán)重序并且是尺寸已知的。

浮點數(shù)只以本機格式存在。因為浮點格式的千差萬別而且缺乏一種標(biāo)準(zhǔn)的“網(wǎng)絡(luò)”表現(xiàn)形式,所以沒有做任何浮點數(shù)交換的工具。這意味著在一臺機器上打包了的浮點數(shù)數(shù)據(jù)可能不能在另外一臺上讀。甚至如果兩臺機器都使用 IEEE 浮點數(shù)算法的話,這都仍然是一個問題,因為與權(quán)重相關(guān)的內(nèi)存表現(xiàn)形式不是 IEEE 規(guī)范的一部分。

Perl 在內(nèi)部使用雙精度數(shù)進行所有浮點數(shù)計算,所以從 double 轉(zhuǎn)換成 float,然后又轉(zhuǎn)換成 float 會損失精度。這就意味著 unpack("f", pack("f", $foo)) 可能不等于 $foo。

你有責(zé)任為其他程序考慮任何對齊或填充的問題,尤其是那些帶著 C 編譯器自己的異質(zhì)概念的 C struct 的程序,C 編譯器在不同的體系結(jié)構(gòu)上對 C struct 如何布局有著巨大的差別。你可能需要在打包時增加足夠的 x 來彌補這個問題。比如,一個 C 聲明:

   struct foo {
unsigned char c;
float f;
};

可以寫成一個“C x f”格式,一個“C x3 f”格式,或者甚至是“f C”格式——而且這只是其中一部分。pack 和 unpack 函數(shù)把它們的輸入和輸出當(dāng)作平面的字節(jié)序列處理,因為它們不知道這些字節(jié)要去哪兒,或者從哪兒來。

讓我們看一些例子,下面的第一部分把數(shù)字值包裝成字節(jié):

   $out = pack "CCCC", 65, 66, 67, 68;      # $out 等于"ABCD"
$out = pack "C4", 65, 66, 67, 68;         # 一樣的東西

下面的對 Unicode 的循環(huán)字母做同樣的事情:

   $foo = pack("U4", 0x24b6, 0x24b7, 0x24b8, 0x24b9);

下面的做類似的事情,增加了一些空:

   $out = pack "CCxxCC", 65, 66, 67, 68;      # $out 等于 "AB\0\0CD"

打包你的短整數(shù)并不意味著你就可移植了:

   $out = pack "s2", 1, 2;         # 在小頭在前的機器上是 "\1\0\2\0"
# 在大頭在前的機器上是 "\0\1\0\2"

在二進制和十六進制包裝上,count 指的是位或者半字節(jié)的數(shù)量,而不是生成的字節(jié)數(shù)量:

   $out = pack "B32", "...(略)";
$out = pack "H8", "5065726c";         # 都生成“Perl”

a 域里的長度只應(yīng)用于一個字串:

   $out = pack "a4", "abcd", "x", "y", "z";      # "abcd"

要繞開這個限制,使用多倍聲明:

   $out = pack "aaaa",    "abcd", "x", "y", "z";   # "axyz"
$out = pack "a" x 4,   "abcd", "x", "y", "z";   # "axyz"

a 格式做空填充:

   $out = pack "a14", "abcdefg";         # " abcdefg\0\0\0\0\0\0"

這個模板打包一個 C 的 struct tm 記錄(至少在一些系統(tǒng)上如此):

   $out = pack "i9pl", gmtime(), $tz, $toff;

通常,同樣的模板也可以在 unpack 函數(shù)里使用,盡管一些模板的動作不太一樣,特別是 a,A,和 Z。

如果你想把定長文本域連接到一起,可以用 TEMPLATE 是多個 a 或者 A 的 pack:

$string = pack("A10" x 10, @data);

如果你想用一個分隔符連接變長文本域,那么可以用 join 函數(shù):

   $string = join(" and ", @data);
$string = join ("", @data);         #  空分隔符

盡管我們所有的例子都使用文本字串做模板,但我們沒有理由不讓你使用來自磁盤文件的模板。你可以基于這個函數(shù)做一個完整的關(guān)系型數(shù)據(jù)庫。(我們不會想知道那樣能證明你什么東西。)

解釋摘自<perl編程語言第三版>O‘REILLY出版

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多