perlapio - Perl 的 IO 抽象介面。
#define PERLIO_NOT_STDIO 0 /* For co-existence with stdio only */
#include <perlio.h> /* Usually via #include <perl.h> */
PerlIO *PerlIO_stdin(void);
PerlIO *PerlIO_stdout(void);
PerlIO *PerlIO_stderr(void);
PerlIO *PerlIO_open(const char *path,const char *mode);
PerlIO *PerlIO_fdopen(int fd, const char *mode);
PerlIO *PerlIO_reopen(const char *path, /* deprecated */
const char *mode, PerlIO *old);
int PerlIO_close(PerlIO *f);
int PerlIO_stdoutf(const char *fmt,...)
int PerlIO_puts(PerlIO *f,const char *string);
int PerlIO_putc(PerlIO *f,int ch);
SSize_t PerlIO_write(PerlIO *f,const void *buf,size_t numbytes);
int PerlIO_printf(PerlIO *f, const char *fmt,...);
int PerlIO_vprintf(PerlIO *f, const char *fmt, va_list args);
int PerlIO_flush(PerlIO *f);
int PerlIO_fill(PerlIO *f);
int PerlIO_eof(PerlIO *f);
int PerlIO_error(PerlIO *f);
void PerlIO_clearerr(PerlIO *f);
int PerlIO_getc(PerlIO *d);
int PerlIO_ungetc(PerlIO *f,int ch);
SSize_t PerlIO_read(PerlIO *f, void *buf, size_t numbytes);
Size_t PerlIO_unread(PerlIO *f,const void *vbuf, size_t count
int PerlIO_fileno(PerlIO *f);
void PerlIO_setlinebuf(PerlIO *f);
Off_t PerlIO_tell(PerlIO *f);
int PerlIO_seek(PerlIO *f, Off_t offset, int whence);
void PerlIO_rewind(PerlIO *f);
int PerlIO_getpos(PerlIO *f, SV *save); /* prototype changed */
int PerlIO_setpos(PerlIO *f, SV *saved); /* prototype changed */
int PerlIO_fast_gets(PerlIO *f);
int PerlIO_has_cntptr(PerlIO *f);
SSize_t PerlIO_get_cnt(PerlIO *f);
char *PerlIO_get_ptr(PerlIO *f);
void PerlIO_set_ptrcnt(PerlIO *f, char *ptr, SSize_t count);
int PerlIO_canset_cnt(PerlIO *f); /* deprecated */
void PerlIO_set_cnt(PerlIO *f, int count); /* deprecated */
int PerlIO_has_base(PerlIO *f);
char *PerlIO_get_base(PerlIO *f);
SSize_t PerlIO_get_bufsiz(PerlIO *f);
PerlIO *PerlIO_importFILE(FILE *stdio, const char *mode);
FILE *PerlIO_exportFILE(PerlIO *f, const char *mode);
FILE *PerlIO_findFILE(PerlIO *f);
void PerlIO_releaseFILE(PerlIO *f,FILE *stdio);
int PerlIO_apply_layers(pTHX_ PerlIO *f, const char *mode,
const char *layers);
int PerlIO_binmode(pTHX_ PerlIO *f, int ptype, int imode,
const char *layers);
void PerlIO_debug(const char *fmt,...);
Perl 的原始碼和需要最大可移植性的擴充功能應使用上述函式,而非 ANSI C 的 stdio.h 中定義的函式。Perl 標頭檔(特別是「perlio.h」)會在組態時將它們 #define
為選取的 I/O 機制。
這些函式仿照 stdio.h 中的函式,但參數順序已「稍微整理過」。
PerlIO *
取代了 FILE *。和 FILE * 一樣,它應視為不透明的(假設它是指向某個東西的指標應該是安全的)。
目前有兩種實作方式
以上所有都已 #define 到 stdio 函式,或是一些會呼叫 stdio 的簡單包裝函式。在此情況下,只有 PerlIO * 是 FILE *。自 perl5.003_02 中導入抽象化功能以來,這一直是預設的實作方式。
此實作方式在 perl5.7.0 之後才推出,是針對上述抽象化功能的重新實作,讓 Perl 可以更靈活地控制 IO 的執行方式,因為它將 IO 與作業系統和 C 函式庫選擇執行的途徑脫鉤。對於 USE_PERLIO,PerlIO * 有額外的間接層,也就是指標指向指標。這讓 PerlIO * 可以維持已知的值,同時在執行期間更換底層實作。在此情況下,上述所有都是呼叫底層實作的函式(但非常簡單)。
這是唯一會讓 PerlIO_apply_layers()
執行「有趣」動作的實作方式。
USE_PERLIO 實作方式說明在 perliol 中。
由於「perlio.h」是一層薄薄的層(為了效率),這些函式的語意在某種程度上取決於底層實作。如果了解這些變異,會在下方註明。
除非另有說明,否則函式會在成功時傳回 0,或在發生錯誤時傳回負值(通常是 EOF
,通常為 -1)並設定 errno
。
請使用這些函式,而不是 stdin
、stdout
、stderr
。它們寫成看起來像是「函式呼叫」而不是變數,因為如果平台無法將資料匯出到載入的模組,或(例如)不同的「執行緒」可能有不同的值,這會讓它們更容易成為函式呼叫。
這些函數對應到 fopen()/fdopen(),參數也相同。如果有錯誤,會傳回 NULL
並設定 errno
。開啟的處理序數量可能受到實作限制,可能會比開啟檔案數量限制低,如果超過這個限制,傳回 NULL
時,errno
可能不會被設定。
雖然目前兩個實作都有這個函數,但 perl 本身並未使用。由於 perl 沒有使用,所以測試不完全。
Perl 偏好將新的底層描述子複製到現有 PerlIO 所使用的描述子。這可能會成為這個函數在未來的行為。
這些函數等同於 fprintf()/vfprintf()。
這個函數等同於 printf()。printf 已 #define 成這個函數,所以目前在 perl 程式碼中使用 printf(fmt,...)
是合法的。
這些函數在功能上對應到 fread() 和 fwrite(),但參數和傳回值不同。PerlIO_read() 和 PerlIO_write() 的簽章是仿照更健全的底層函數 read() 和 write():首先傳遞「檔案」參數,只有一個「count」,而傳回值可以區分錯誤和 EOF
。
如果成功,會傳回位元組計數(可能是零或正數);如果失敗,會傳回負值並設定 errno
。依據實作,如果作業被訊號中斷,errno
可能會是 EINTR
。
使用下層資料填滿與 f
相關聯的緩衝區。PerlIO_read
在正常運作時會呼叫這個函數。成功時傳回 0;失敗時傳回 -1。
依據實作,如果操作被訊號中斷,errno
可能會是 EINTR
。
這些函式分別對應 fputs() 和 fputc()。請注意,參數已修改,將「檔案」放在第一個。
這個函式對應 ungetc()。請注意,參數已修改,將「檔案」放在第一個。安排下一次的讀取操作會傳回位元組 c。儘管名稱中暗示「字元」,但只有 0..0xFF 範圍內的數值才有定義。如果成功,會傳回位元組 c;如果失敗,會傳回 -1 (EOF
)。可以「推回」的位元組數目可能會有所不同,只有 1 個字元是確定的,而且只有當它是從處理常式讀取的最後一個字元時才成立。
這個函式允許一次取消讀取多於一個位元組。它會有效地將 count
個位元組從 buf
緩衝區的開頭取消讀取,以便下一次的讀取操作會在緩衝區中其他任何內容之前傳回這些位元組。
傳回未讀取的位元組數目。
這個函式對應 getc()。儘管名稱中含有 c,但只支援 0..0xFF 範圍的位元組。傳回讀取的字元,如果失敗,會傳回 -1 (EOF
)。
這個函式對應 feof()。傳回一個 true/false 指示,表示處理常式是否在檔案結尾。對於終端裝置,這可能會或可能不會是「黏著的」,具體取決於實作。這個旗標會由 PerlIO_seek() 或 PerlIO_rewind() 清除。
這個函式對應 ferror()。傳回一個 true/false 指示,表示處理常式是否發生 IO 錯誤。
這個函式對應 fileno(),請注意,在某些平台上,「fileno」的意義可能與 Unix 不符。如果處理常式沒有與其關聯的開啟描述子,會傳回 -1。
這個函式對應 clearerr(),也就是說,它會清除「串流」的「錯誤」和(通常)「eof」旗標。不會傳回值。
這對應到 fflush()。將任何緩衝寫入資料傳送到基礎檔案中。如果使用 NULL
呼叫,這可能會清除所有開啟的串流 (或在某些 USE_STDIO 實作中會核心傾印)。在僅開啟用於讀取的控制代碼上呼叫,或最後一個操作為某種類型的讀取時,在某些 USE_STDIO 實作中可能會導致未定義的行為。USE_PERLIO (層) 實作會嘗試表現得更好:傳遞 NULL
時,它會清除所有開啟的串流,並嘗試將資料保留在讀取串流中 (在緩衝區中或透過搜尋控制代碼到目前的邏輯位置)。
這對應到 fseek()。將緩衝寫入資料傳送到基礎檔案,或捨棄任何緩衝讀取資料,然後根據 offset 和 whence (sic) 指定的位置來定位檔案描述符。當在同一個控制代碼上切換讀取和寫入時,這是正確的做法 (請參閱上方 PerlIO_flush() 的問題)。Offset 的類型為 Off_t
,這是一個 perl Configure 值,可能與 stdio 的 off_t
不同。
這對應到 ftell()。傳回目前的檔案位置,或在發生錯誤時傳回 (Off_t) -1。可能會只傳回系統「已知」的值,而不會進行系統呼叫或檢查基礎檔案描述符 (因此,在共用檔案描述符上使用並不安全,除非使用 PerlIO_seek())。傳回值的類型為 Off_t
,這是一個 perl Configure 值,可能與 stdio 的 off_t
不同。
這些 (鬆散地) 對應到 fgetpos() 和 fsetpos()。它們期望傳遞「Perl 標量值」,而不是 stdio 的 Fpos_t。儲存在那裡的內容應被視為不透明。資料的配置可能因控制代碼而異。在不使用 stdio 或平台沒有 stdio 呼叫時,它們會以 PerlIO_tell() 和 PerlIO_seek() 的形式實作。
這對應到 rewind()。它通常定義為
PerlIO_seek(f,(Off_t)0L, SEEK_SET);
PerlIO_clearerr(f);
這對應到 tmpfile(),也就是傳回一個匿名的 PerlIO 或在發生錯誤時傳回 NULL。系統會在關閉時自動嘗試刪除檔案。在 Unix 中,檔案通常會在建立後立即被 unlink
,因此不論如何關閉檔案都無所謂。在其他系統中,檔案可能只有在透過 PerlIO_close() 關閉和/或程式透過 exit
退出時才會被刪除。視實作而定,可能會出現允許其他程序存取檔案的「競爭條件」,不過一般而言,這方面會比臨時方案更安全。
這對應到 setlinebuf()。不會傳回值。什麼構成「一行」取決於實作,但通常表示寫入「\n」會清空緩衝區。像「this\nthat」這類東西會發生什麼事則不確定。(Perl 核心只在「傾印」時使用它;它與 $| 自動沖刷無關。)
PerlIO 與 stdio 共存有輪廓支援。顯然地,如果 PerlIO 是以 stdio 實作,就不會有問題。然而,在其他情況下,必須有建立 FILE * 的機制,才能傳遞給將使用 stdio 呼叫的函式庫程式碼。
第一步是加入這行
#define PERLIO_NOT_STDIO 0
在包含任何 perl 標頭檔之前。(這可能在某個時間點成為預設值)。這會防止「perlio.h」嘗試將 stdio 函式 #define 到 PerlIO 函式。
如果 XS 程式碼預期 FILE * 引數,則可能最好使用「typemap」。標準 typemap 會調整為理解此領域的任何變更。
用於從 FILE * 取得 PerlIO *。
mode 引數應該是會傳遞給 fopen/PerlIO_open 的字串。如果它是 NULL,則為了支援舊版,程式碼會(視平台和實作而定)嘗試以經驗方式判斷 f 開啟的模式,或使用「r+」表示讀寫串流。
呼叫後,FILE * 只能透過呼叫傳回的 PerlIO * 上的 PerlIO_close()
來關閉。
PerlIO 已設定為文字模式。如果這不是您要的模式,請使用 PerlIO_binmode。
這不是 PerlIO_exportFILE() 的反向操作。
給定 PerlIO *,建立一個「原生」FILE *,適合傳遞給預期編譯並連結到 ANSI C stdio.h 的程式碼。mode 參數應為字串,就像傳遞給 fopen/PerlIO_open 一樣。如果它是 NULL,則為了相容性,FILE * 會以與 PerlIO * 相同的模式開啟。
已「匯出」此類 FILE * 的事實已記錄下來(通常是透過將新的 :stdio「層」推送到 PerlIO *),這可能會影響 PerlIO * 上的後續 PerlIO 作業。您不應對檔案呼叫 fclose()
,除非您呼叫 PerlIO_releaseFILE()
以解除它與 PerlIO * 的關聯。(不要使用 PerlIO_importFILE() 來解除關聯。)
重複呼叫此函式會在每次呼叫時建立一個 FILE *(並每次都會推入一個 :stdio 層)。
呼叫 PerlIO_releaseFILE 會通知 PerlIO,FILE * 的所有使用都已完成。它會從「已匯出」FILE * 的清單中移除,而關聯的 PerlIO * 應回復為其原始行為。
使用此函式解除與 PerlIO * 的關聯,而該 PerlIO * 是使用 PerlIO_exportFILE() 關聯的。
傳回由 stdio 層使用的原生 FILE *。如果沒有,它會使用 PerlIO_exportFILE 建立一個。無論哪種情況,FILE * 都應視為屬於 PerlIO 子系統,且只能透過呼叫 PerlIO_close()
來關閉。
除了目前為止定義的標準類似 API 之外,還有一個「實作」介面,讓 perl 能取得 PerlIO 的內部資訊。下列呼叫對應於 Configure 所決定的各種 FILE_xxx 巨集,或其他實作中的等效巨集。此部分僅對那些關注 perl 核心詳細行為、實作 PerlIO 對應,或撰寫程式碼以利用 IO 系統所執行的「預先讀取」的人感興趣,就像 perl 所做的一樣。請注意,使用這些介面的任何程式碼都必須準備好以傳統方式執行,如果控制代碼不支援這些介面。
如果實作有所有介面,讓 perl 的 sv_gets
能「繞過」正常的 IO 機制,則傳回 true。這可能因處理而異。
PerlIO_fast_gets(f) = PerlIO_has_cntptr(f) && \
PerlIO_canset_cnt(f) && \
'Can set pointer into buffer'
實作可以傳回「緩衝區」中目前位置的指標,以及緩衝區中可用位元組的計數。不要使用這個 - 使用 PerlIO_fast_gets。
傳回緩衝區中可讀取位元組的計數。零或負數傳回表示沒有更多可用位元組。
傳回緩衝區中下一個可讀取位元組的指標,只有在 PerlIO_get_cnt() 傳回正值時,透過指標存取(解除參考)才是安全的。只允許正向偏移,且偏移量必須小於或等於 PerlIO_get_cnt() 傳回的值。
設定緩衝區中的指標,以及緩衝區中仍有的位元組計數。應該只用於設定指標在先前呼叫 PerlIO_get_ptr
和 PerlIO_get_cnt
所暗示的範圍內。這兩個值必須彼此一致(實作可能只使用其中一個,或可能需要兩個)。
實作可以調整其在緩衝區中位元組數量的概念。不要使用這個 - 使用 PerlIO_fast_gets。
模糊 - 設定緩衝區中的位元組計數。已不建議使用。只有在 PerlIO_canset_cnt() 傳回 true 時才能使用。目前只在 doio.c 中使用,用於強制將小於 -1 的計數設定為 -1。或許應該改為 PerlIO_set_empty 或類似的名稱。如果「計數」是由指標和「限制」推論出來的,這個呼叫實際上可能什麼都不做。不要使用這個 - 使用 PerlIO_set_ptrcnt()。
如果實作有一個緩衝區,並且可以傳回整個緩衝區的指標及其大小,則傳回 true。perl 用於 -T / -B 測試。其他用途將非常模糊...
傳回緩衝區的開始。僅存取緩衝區中 PerlIO_get_bufsiz() 傳回的值之前的正位移。
傳回緩衝區中的總位元組數,這既不是可讀取的位元組數,也不是分配給緩衝區的記憶體量。相反地,這是作業系統和/或實作上次要求 IO 時碰巧read()
(或其他) 的內容。
USE_PERLIO 實作的新介面。層級「:crlf」和「:raw」是其他實作唯一允許的層級,而且會被靜默忽略。(從 perl5.8 開始,已不建議使用「:raw」)。在可攜式案例中,請使用下方的 PerlIO_binmode()。
perl 的binmode
運算子使用的掛勾。ptype 是 perl 用於表示 IO 類型的字元
imode 是 O_BINARY
或 O_TEXT
。
layers 是要套用的層級字串;在非 USE_PERLIO 案例中,只有「:crlf」有意義。(從 perl5.8 開始,已不建議使用「:raw」,建議傳遞 NULL。)
可攜式案例為
PerlIO_binmode(aTHX_ f,ptype,O_BINARY,NULL);
and
PerlIO_binmode(aTHX_ f,ptype,O_TEXT,":crlf");
在 Unix 中,這些呼叫可能完全沒有任何作用。在其他地方,它們會將「\n」改為 CR、LF 轉換,並可能導致在讀取時寫入或尊重特殊的文字「檔案結束」指標。在對控制代碼執行任何 IO 之後進行呼叫的效果取決於實作。(它可能會被忽略,也會影響任何已快取的資料,或僅套用於後續資料。)
PerlIO_debug 是一個類似 printf() 的函式,可供用於偵錯。沒有傳回值。其主要用途是在 PerlIO 中,在 PerlIO 中使用真正的 printf、warn() 等會遞迴呼叫 PerlIO,並會造成問題。
PerlIO_debug 會寫入由 $ENV{'PERLIO_DEBUG'} 指定的檔案,或是在未定義環境變數的情況下預設為 stderr。典型的用法可能是
Bourne shells (sh, ksh, bash, zsh, ash, ...):
PERLIO_DEBUG=/tmp/perliodebug.log ./perl -Di somescript some args
Csh/Tcsh:
setenv PERLIO_DEBUG /tmp/perliodebug.log
./perl -Di somescript some args
If you have the "env" utility:
env PERLIO_DEBUG=/tmp/perliodebug.log ./perl -Di somescript args
Win32:
set PERLIO_DEBUG=perliodebug.log
perl -Di somescript some args
在未建置 -DDEBUGGING
的 Perl 中,或是在未指定 -Di
命令列開關,或是在污染的情況下,PerlIO_debug() 是無操作的。