Perl提供了一個Storable模塊,用來對數據結構進行序列化(serialization,Perl中稱為凍結),也就是將數據結構保存為二進制數據。
- 序列化后的數據可以寫入文件實現持久化,可以將持久化文件拷貝給遠程機器
- 也可以通過網絡套接字將序列化數據傳遞給遠程機器
- 序列化后的數據在任意機器上都可以反序列化(deserialization,Perl中稱為解凍)得到原始的數據結構
- 序列化數據結構時是進行深拷貝的,序列化完成后,修改原始數據,不會影響反序列化的結果
序列化:freeze、nfreeze和thaw
Storabel的freeze和thaw函數分別用來凍結(序列化)和解凍(反序列化):
- freeze凍結的是數據對象,不包括它們的引用和名稱
- freeze的參數需要是引用,可以是多個引用參數,返回的是二進制的凍結序列,各數據結構序列化在不同行
- thaw的參數是引用變量,返回的是一個匿名列表,列表各元素對應freeze凍結時的各數據結構
- nfreeze也是凍結,但是按照網絡字節序進行序列化,適合遠程傳輸序列化時的標量名稱,見下一小節對主機字節序和網絡字節序的描述
#!/usr/bin/perl
use 5.010;
use Storable qw(freeze thaw);
%hash=(
'longshuai'=>{
'gender'=>'male',
'age' =>18,
'prov' =>'jiangxi',
},
'wugui'=>{
'gender'=>'male',
'age' =>20,
'prov' =>'zhejiang',
},
'xiaofang'=>{
'gender'=>'female',
'age' =>19,
'prov' =>'fujian',
},
);
@name=('fairy',[qw(longshuai wugui xiaofang)]);
$frozen = freeze [\%hash,\@name]; # 凍結引用,返回一個凍結后的列表
#say $frozen; # 輸出一堆亂碼
$thaw_out=thaw($frozen); # 解凍,返回引用列表
say $thaw_out->[0]; # 輸出:HASH(0x557171a4cff8)
say $thaw_out->[0]{wugui}{prov}; # 輸出:zhejiang
say $thaw_out->[1]; # 輸出:ARRAY(0x557171a4d220)
say $thaw_out->[1][1][2]; # 輸出:xiaofang
上面的示例中,使用freeze凍結兩個數據結構后,凍結后的二進制數據內容將賦值給一個標量變量,注意它返回的是類似於字符串那種形式的,只不過這段字符串是二進制格式的。
使用thaw解凍后,將返回一個匿名列表,列表中的元素是各凍結的數據結構的引用。對於上面的示例來說,返回值類似如此結構[$ref_hash,$ref_name]
,將其賦值給一個引用變量$thaw_out
,然后就可以通過$thaw_out->[0]
和$thaw_out->[1]
分別訪問這兩個引用。
如下圖描述:
freeze序列化過程:
thaw反序列化過程:
持久化:store、nstore和retrieve
Storable模塊可以將數據結構序列化后持久化保存到文件中,或通過TCP套接字傳輸出去。
store和nstore用於將序列化數據進行持久化,它們用法一樣,如下:
store \%ref_hash, 'file';
store [\%ref_hash,\@ref_arr], 'file';
nstore \%ref_hash, 'file';
nstore [\%ref_hash,\@ref_arr], 'file';
但是store存儲序列化數據時默認采用的是主機字節序(host byte order),nstore默認采用的是網絡字節序(network byte order),采用網絡字節序可以保證被TCP套接字傳輸出去時,遠程主機能以完全一致的字節序方式讀取數據。所以,要想通過網絡傳輸序列化的對象時,需要使用nstore。
小知識:主機字節序和網絡字節序
多字節數據對象在存儲時,必須考慮兩個問題:
- 這段數據對象要存儲到哪個地址
- 存儲時如何排列這些字節
這里不考慮存儲的地址問題。對於待存儲的數值"0x1122"來說,11屬於高位字節,22屬於低位字節。對於存儲時考慮以何種字節排列方式來說,有兩種方式:大端字節序和小端字節序。假設要存儲的數據對象"0x1234567":
- 大端字節序(big-endian):存儲時,高位在前,低位在后,所以存儲的時候,和上面源數據格式一樣"01 23 45 67"
- 小端字節序(little-endian):存儲時,高位在后,低位在前,所以存儲的時候,和上面源數據格式相反"67 45 23 01"
大端字節序對人類來說比較容易理解,但幾乎所有計算機都是采用小端字節序存儲的,所以也稱為主機字節序。而TCP/IP協議規定,網絡傳輸時的網絡字節序都采用大端字節序傳輸,這樣一來所有網絡傳輸的數據都規范化,遠程主機總會按照大端字節序去讀取傳輸過來的數據。
store和nstore持久化的序列化數據可以通過retrieve函數讀取並反序列化。retrieve返回的值和thaw的返回結果是一樣的。
my $ref_list = retrieve 'file';
以下是nstore和retrieve的一個示例:
#!/usr/bin/perl
use 5.010;
use Storable qw(nstore retrieve);
%hash=(
'longshuai'=>{
'gender'=>'male',
'age' =>18,
'prov' =>'jiangxi',
},
'wugui'=>{
'gender'=>'male',
'age' =>20,
'prov' =>'zhejiang',
},
'xiaofang'=>{
'gender'=>'female',
'age' =>19,
'prov' =>'fujian',
},
);
@name=('fairy',[qw(longshuai wugui xiaofang)]);
nstore [\%hash,\@name],'/tmp/tmp_data'; # 將數據序列化並持久化到文件
$ref_list=retrieve '/tmp/tmp_data'; # 反序列化並讀取數據
say $ref_list->[0]; # 輸出:HASH(0x55aee8340318)
say $ref_list->[0]{wugui}{prov}; # 輸出:zhejiang
say $ref_list->[1]; # 輸出:ARRAY(0x55aee8340480)
say $ref_list->[1][1][2]; # 輸出:xiaofang
序列化到文件描述符:store_fd、nstore_fd和fd_retrieve
- store_fd、nstore_fd用於將數據結構凍結到指定的文件描述符,比如文件、管道、套接字、字符串中
- retrieve_fd則從給定的文件描述符中讀取
下面示例涉及到文件句柄的標量引用,如不理解,請跳過。
例如,將數據結構凍結到一個字符串$string
中存儲起來:
use Storable;
open my $string_fh,">",\my $string or die "...$!";
nstore_fd \@data,$string_fh;
close $string_fh;
從持久化數據的變量$string
中解凍:
open my $string_fh1,"<",\$string or die "$!";
$new_hash = fd_retrieve($string_fh1);
close $string_fh1;
如此,數據結構就存儲到$new_hash
這個引用中。
例如,將前文的數據結構%hash
存儲起來,並立即解凍:
#!/usr/bin/perl
use Storable qw(store_fd nstore_fd fd_retrieve);
use Data::Dumper;
open my $string_fh,">",\my $string;
nstore_fd [\%hash],$string_fh;
close $string_fh;
print Dumper($string); # 輸出:一大堆亂碼二進制
open my $string_fh1,"<",\$string or die "$!";
$new_hash = fd_retrieve($string_fh1);
close $string_fh1;
print Dumper($new_hash); # 輸出:hash數據結構
深拷貝:dclone
關於淺拷貝、深拷貝,見Perl的淺拷貝和深度拷貝