目錄

名稱

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 ....

說明

當在 openbinmode 層規格中遇到未定義的層 'foo' 時,C 程式碼會執行等同於下列動作:

use PerlIO 'foo';

PerlIO.pm 中的 Perl 程式碼接著嘗試透過下列動作來尋找層:

require PerlIO::foo;

否則,PerlIO 套件是額外 PerlIO 相關函式的佔位符。

一般來說,PerlIO 層(以前有時稱為「準則」)是應用於檔案句柄(指定為以空格或冒號分隔的清單,慣例上以冒號開頭)的有序堆疊。每個層對任何輸入或輸出執行一些操作,除非使用 sysreadsyswrite 等方式繞過。讀取操作會按照設定順序(由左至右)通過堆疊,而寫入操作則會以相反順序通過堆疊。

有些層實際上只會設定下層的旗標,或修改目前的堆疊,但本身並不會持續存在於堆疊上;這些層稱為偽層。

開啟一個句柄時,它會使用在 open() 呼叫中明確指定的任何層(或平台預設值,如果指定為冒號而沒有後續層)。

如果沒有明確指定層,則會使用 ${^OPEN} 變數指定的層開啟句柄(通常使用 open pragma 設定詞法範圍,或使用 -C 命令列開關或 PERL_UNICODE 環境變數設定主程式範圍)。

如果在 open() 呼叫或 ${^OPEN} 變數中沒有指定層,則會使用為該架構設定的預設層堆疊開啟句柄;請參閱 "預設值和如何覆寫它們"

某些層會自動插入必要的較低層級層,如果不存在的話;例如 :perlio 會在它下方插入 :unix 以進行低層級 IO,而 :encoding 會插入緩衝 IO 的平台預設值。

可以在開啟的句柄上呼叫 binmode 函數,以將其他層推送到堆疊上,這也可能會修改現有層。沒有層呼叫的 binmode 會移除或取消設定任何轉換位元組串流的現有層,使句柄適合於二進位資料。

目前定義了以下層

:unix

最低層級層,提供基本的 PerlIO 作業,以 UNIX/POSIX 數字檔案描述符呼叫(open()、read()、write()、lseek()、close())表示。它甚至在非 Unix 架構上使用,而大多數其他層都在它上面操作。

:stdio

呼叫 freadfwritefseek/ftell 等的層。請注意,由於這是「真正的」stdio,它會忽略它底下的任何層,並透過 C 函式庫直接進入作業系統,就像往常一樣。此層實作低層級 IO 和緩衝,但在現代架構上很少使用。

:perlio

PerlIO 緩衝區的從頭實作。提供快速存取緩衝區的 sv_gets,實作 Perl 的 readline/<>,並一般而言嘗試將資料複製減至最少。

:perlio 會在自身下方插入一個 :unix 層,以執行低階 IO。

:crlf

實作 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: $!";
:utf8

偽層,宣告串流接受 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 字元之間轉換。

:bytes

這是 :utf8 偽層的相反。它會關閉下方層的旗標,以便從中讀取的資料會被視為 Perl 的內部降級編碼,因此會被解釋為 Latin-1 或 EBCDIC 的原生單位元組編碼。同樣地,如果將「寬」字元(不在 0..255 範圍內的碼點)寫入此類串流,Perl 會發出警告。

使用 :encoding 層將其推送到控制代碼上是很危險的,因為此類層假設與 Perl 的內部升級編碼一起使用,因此您可能會得到一個混亂的結果。請改用 :raw:pop 來移除編碼層。

:raw

: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 轉換。

:pop

一個移除最上層的偽層。提供 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(ENCODING) 透明地執行字元集和編碼轉換,例如從 Shift-JIS 轉換為 Unicode。請注意,:encoding 也會啟用 :utf8。請參閱 PerlIO::encoding 以取得更多資訊。

:mmap

一個層,透過使用 mmap() 來實作檔案的「讀取」,讓(整個)檔案出現在處理程序的位址空間中,然後將其用作 PerlIO 的「緩衝區」。在某些情況下,對於大型檔案來說,可能會更快,而且當多個處理程序正在讀取同一個檔案時,可能會減少實體記憶體使用量。

無法使用 mmap() 的檔案會回復為像 :perlio 層一樣的行為。寫入行為也像 :perlio 層一樣,因為寫入的 mmap() 需要額外的家務管理(來延伸檔案),這會抵消任何優點。

如果平台不支援 mmap():mmap 層將不存在。請參閱 PerlIO::mmap 以取得更多資訊。

:via

:via(MODULE) 允許透過任意 Perl 模組套用轉換,例如壓縮/解壓縮、加密/解密。請參閱 PerlIO::via 以取得更多資訊。

:scalar

一個層,使用純量變數實作「記憶體中」檔案,在開啟此類處理程序時自動用於取代 IO 的平台預設值。因此,預期純量會像檔案一樣作用,只包含或儲存位元組。請參閱 PerlIO::scalar 以取得更多資訊。

raw 的替代方案

取得二進位串流的替代方法是使用

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」perlunicodeperliolEncode