這個函數(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出版