PerlIO - PerlIO 層的隨選載入器和 PerlIO::* 名稱空間的根目錄
# support platform-native and CRLF text files
open(my $fh, "<:crlf", "my.txt") or die "open failed: $!";
# append UTF-8 encoded text
open(my $fh, ">>:encoding(UTF-8)", "some.log")
or die "open failed: $!";
# portably open a binary file for reading
open(my $fh, "<", "his.jpg") or die "open failed: $!";
binmode($fh) or die "binmode failed: $!";
Shell:
PERLIO=:perlio perl ....
當在 open
或 binmode
層規格中遇到未定義的層 'foo' 時,C 程式碼會執行等同於下列動作:
use PerlIO 'foo';
PerlIO.pm 中的 Perl 程式碼接著嘗試透過下列動作來尋找層:
require PerlIO::foo;
否則,PerlIO
套件是額外 PerlIO 相關函式的佔位符。
一般來說,PerlIO 層(以前有時稱為「準則」)是應用於檔案句柄(指定為以空格或冒號分隔的清單,慣例上以冒號開頭)的有序堆疊。每個層對任何輸入或輸出執行一些操作,除非使用 sysread
或 syswrite
等方式繞過。讀取操作會按照設定順序(由左至右)通過堆疊,而寫入操作則會以相反順序通過堆疊。
有些層實際上只會設定下層的旗標,或修改目前的堆疊,但本身並不會持續存在於堆疊上;這些層稱為偽層。
開啟一個句柄時,它會使用在 open() 呼叫中明確指定的任何層(或平台預設值,如果指定為冒號而沒有後續層)。
如果沒有明確指定層,則會使用 ${^OPEN} 變數指定的層開啟句柄(通常使用 open pragma 設定詞法範圍,或使用 -C
命令列開關或 PERL_UNICODE
環境變數設定主程式範圍)。
如果在 open() 呼叫或 ${^OPEN}
變數中沒有指定層,則會使用為該架構設定的預設層堆疊開啟句柄;請參閱 "預設值和如何覆寫它們"。
某些層會自動插入必要的較低層級層,如果不存在的話;例如 :perlio
會在它下方插入 :unix
以進行低層級 IO,而 :encoding
會插入緩衝 IO 的平台預設值。
可以在開啟的句柄上呼叫 binmode
函數,以將其他層推送到堆疊上,這也可能會修改現有層。沒有層呼叫的 binmode
會移除或取消設定任何轉換位元組串流的現有層,使句柄適合於二進位資料。
目前定義了以下層
最低層級層,提供基本的 PerlIO 作業,以 UNIX/POSIX 數字檔案描述符呼叫(open()、read()、write()、lseek()、close())表示。它甚至在非 Unix 架構上使用,而大多數其他層都在它上面操作。
呼叫 fread
、fwrite
和 fseek
/ftell
等的層。請注意,由於這是「真正的」stdio,它會忽略它底下的任何層,並透過 C 函式庫直接進入作業系統,就像往常一樣。此層實作低層級 IO 和緩衝,但在現代架構上很少使用。
PerlIO 緩衝區的從頭實作。提供快速存取緩衝區的 sv_gets
,實作 Perl 的 readline/<>,並一般而言嘗試將資料複製減至最少。
:perlio
會在自身下方插入一個 :unix
層,以執行低階 IO。
實作 DOS/Windows 類似 CRLF 行尾的層。在讀取時,將 CR,LF 對轉換為單一 "\n" 新行字元。在寫入時,將每個 "\n" 轉換為 CR,LF 對。請注意,此層會靜默拒絕被推送到自身上方。
它目前不模擬 MS-DOS,不將 Control-Z 視為檔案結束標記。
在 DOS/Windows 類似架構中,此層為預設值的一部分,它也像 :perlio
層,移除 CRLF 轉換(例如使用 :raw
)只會取消設定 CRLF 轉換標記。自 Perl 5.14 起,您也可以稍後套用另一個 :crlf
層,例如在編碼層之後必須進行 CRLF 轉換時。在其他架構中,它是一個平凡的 CRLF 轉換層,可以正常新增和移除。
# translate CRLF after encoding on Perl 5.14 or newer
binmode $fh, ":raw:encoding(UTF-16LE):crlf"
or die "binmode failed: $!";
偽層,宣告串流接受 Perl 的字元內部升級編碼,這在 ASCII 機器上大約是 UTF-8,但在 EBCDIC 機器上是 UTF-EBCDIC。這允許 Perl 可以表示的任何字元從串流讀取或寫入串流。
此層(實際上會設定前一層的標記,且會由任何 :encoding
層隱含設定)不會轉換或驗證位元組序列。它反而表示位元組串流會由其他層安排,以 Perl 的內部升級編碼提供,Perl 程式碼(和正確撰寫的 XS 程式碼)會將其解譯為已解碼的 Unicode 字元。
注意:請勿使用此層從 UTF-8 位元組轉換,因為無效的 UTF-8 或二進位資料會導致格式錯誤的 Perl 字串。用於輸出的時候不太可能產生無效的 UTF-8,儘管它會在 EBCDIC 系統中產生 UTF-EBCDIC。建議使用 :encoding(UTF-8)
層(連字號很重要),因為它會確保在有效的 UTF-8 位元組和有效的 Unicode 字元之間轉換。
這是 :utf8
偽層的相反。它會關閉下方層的旗標,以便從中讀取的資料會被視為 Perl 的內部降級編碼,因此會被解釋為 Latin-1 或 EBCDIC 的原生單位元組編碼。同樣地,如果將「寬」字元(不在 0..255 範圍內的碼點)寫入此類串流,Perl 會發出警告。
使用 :encoding
層將其推送到控制代碼上是很危險的,因為此類層假設與 Perl 的內部升級編碼一起使用,因此您可能會得到一個混亂的結果。請改用 :raw
或 :pop
來移除編碼層。
:raw
偽層被定義為與呼叫 binmode($fh)
相同 - 串流適合傳遞二進位資料,也就是說每個位元組都按原樣傳遞。串流仍會被緩衝(但在 Perl 5.14 之前並非總是如此)。
在 Perl 5.6 和一些書籍中,:raw
層被記載為 :crlf
層的相反。這不再是事實 - 其他會改變串流二進位性質的層也會被停用。如果您希望在通常會進行 CRLF 轉換的平台上使用 UNIX 行尾,但仍想要 UTF-8 或編碼預設值,適當的做法是將 :perlio
加入 PERLIO 環境變數,或使用該層明確開啟控制代碼,以取代平台預設的 :crlf
。
:raw
的實作是一個偽層,當「推入」時會彈出自己,然後彈出任何會修改二進位資料串流的層。(取消 :utf8
和 :crlf
可能會透過清除旗標而不是彈出層來實作,但那是實作細節。)
由於 :raw
通常會彈出層,因此通常只有在層規格中將它作為唯一或第一個元素才有意義。當用作第一個元素時,它會提供一個已知的基礎來建構,例如
open(my $fh,">:raw:encoding(UTF-8)",...)
or die "open failed: $!";
將建構一個「二進位」串流,而不論平台預設值,但接著會啟用 UTF-8 轉換。
一個移除最上層的偽層。提供 Perl 程式碼一種操作層堆疊的方式。請注意,:pop
僅作用於真實層,不會取消 :utf8
等偽層或旗標的效果。以下是可能的用途範例
open(my $fh,...) or die "open failed: $!";
...
binmode($fh,":encoding(...)") or die "binmode failed: $!";
# next chunk is encoded
...
binmode($fh,":pop") or die "binmode failed: $!";
# back to un-encoded
需要一個更優雅(且更安全的)介面。
除了上述內建的層之外,還可以撰寫自訂層,無論是使用 C/XS 或 Perl,作為名為 PerlIO::<layer name>
的模組。Perl 發行版附帶了一些自訂層。
使用 :encoding(ENCODING)
透明地執行字元集和編碼轉換,例如從 Shift-JIS 轉換為 Unicode。請注意,:encoding
也會啟用 :utf8
。請參閱 PerlIO::encoding 以取得更多資訊。
一個層,透過使用 mmap()
來實作檔案的「讀取」,讓(整個)檔案出現在處理程序的位址空間中,然後將其用作 PerlIO 的「緩衝區」。在某些情況下,對於大型檔案來說,這可能會更快,而且當多個處理程序正在讀取同一個檔案時,可能會減少實體記憶體使用量。
無法使用 mmap()
的檔案會回復為像 :perlio
層一樣的行為。寫入行為也像 :perlio
層一樣,因為寫入的 mmap()
需要額外的家務管理(來延伸檔案),這會抵消任何優點。
如果平台不支援 mmap()
,:mmap
層將不存在。請參閱 PerlIO::mmap 以取得更多資訊。
:via(MODULE)
允許透過任意 Perl 模組套用轉換,例如壓縮/解壓縮、加密/解密。請參閱 PerlIO::via 以取得更多資訊。
一個層,使用純量變數實作「記憶體中」檔案,在開啟此類處理程序時自動用於取代 IO 的平台預設值。因此,預期純量會像檔案一樣作用,只包含或儲存位元組。請參閱 PerlIO::scalar 以取得更多資訊。
取得二進位串流的替代方法是使用
open(my $fh,"<","whatever") or die "open failed: $!";
binmode($fh) or die "binmode failed: $!";
這具有與不使用 PerlIO 或 :raw
有錯誤(就像 Perl 5.14 之前一樣)的舊版 Perl 向後相容的優點。
若要取得未緩衝的串流,請在開啟呼叫中指定未緩衝的層(例如 :unix
)
open(my $fh,"<:unix",$path) or die "open failed: $!";
如果平台類似於 MS-DOS,並且通常會對文字檔案進行 CRLF 至「\n」的轉換,則預設層為
:unix:crlf
否則,如果 Configure
發現如何使用系統的 stdio(在現代架構中並不常見)執行「快速」IO,則預設層級為
:stdio
否則,預設層級為
:unix:perlio
請注意,「預設堆疊」取決於作業系統和 Perl 版本,以及 Perl 的編譯時間和執行時間組態。預設值可透過設定環境變數 PERLIO 為以空白或冒號分隔的層級清單來覆寫,但這無法用於設定需要載入模組(例如 :encoding
)的層級。
這可用於查看各種層級的效應/錯誤,例如
cd .../perl/t
PERLIO=:stdio ./perl harness
PERLIO=:perlio ./perl harness
有關 PERLIO 的各種值,請參閱 perlrun 中的「PERLIO」。
下表摘要說明 UNIX 類似和 DOS 類似平台上的預設層級,以及取決於 $ENV{PERLIO}
的設定
PERLIO UNIX-like DOS-like
------ --------- --------
unset / "" :unix:perlio / :stdio [1] :unix:crlf
:stdio :stdio :stdio
:perlio :unix:perlio :unix:perlio
# [1] ":stdio" if Configure found out how to do "fast stdio" (depends
# on the stdio implementation) and in Perl 5.8, else ":unix:perlio"
下列會傳回檔案處理上 PerlIO 層級的名稱。
my @layers = PerlIO::get_layers($fh); # Or FH, *FH, "FH".
層級會以 open() 或 binmode() 呼叫會使用它們的順序傳回,且不含冒號。
預設會傳回檔案處理輸入端的層級;若要取得輸出端,請使用選用的 output
參數
my @layers = PerlIO::get_layers($fh, output => 1);
(通常檔案處理兩端的層級是相同的,但例如對於 socket,可能會有差異。)
沒有 set_layers(),get_layers() 也不會傳回反映堆疊的繫結陣列,或任何類似花俏的東西。這不是意外或無意的。PerlIO 層級堆疊比單純的堆疊複雜一點(例如,請參閱 :raw
的行為)。您應該使用 open() 和 binmode() 來操作堆疊。
實作細節如下,請閉上您的眼睛。
預設情況下,層級的參數會在層級名稱後以括號傳回,而某些層級(例如 :utf8
)不是真正的層級,而是真實層級上的旗標;若要分別傳回所有這些,請使用選用的 details
參數
my @layer_and_args_and_flags = PerlIO::get_layers($fh, details => 1);
結果將最多是層級數的三倍:第一個元素將是名稱,第二個元素是參數(未指定的參數將是 undef
),第三個元素是旗標,第四個元素又是名稱,以此類推。
您現在可以睜開眼睛了。
Nick Ing-Simmons <nick@ing-simmons.net>
perlfunc 中的「binmode」、perlfunc 中的「open」、perlunicode、perliol、Encode