module Scanf:sig
..end
格式化輸入函式。
模組 Scanf
提供了格式化輸入函式或稱作掃描器。
格式化輸入函式可以從任何種類的輸入中讀取,包括字串、檔案或任何可以返回字元的東西。更通用的字元來源被稱為格式化輸入通道(或掃描緩衝區),其類型為 Scanf.Scanning.in_channel
。更通用的格式化輸入函式可以從任何掃描緩衝區讀取,並被命名為 bscanf
。
一般來說,格式化輸入函式有 3 個參數:
因此,格式化輸入函式 Scanf.bscanf
的典型呼叫是 bscanf ic fmt f
,其中
ic
是一個字元來源(通常是一個類型為 Scanf.Scanning.in_channel
的格式化輸入通道),f
是一個函式,其參數數量與根據 fmt
從輸入中讀取的值的數量相同。如上所述,表達式 bscanf ic "%d" f
從字元來源 ic
讀取一個十進制整數 n
,並返回 f n
。
例如,
stdin
作為字元來源(Scanf.Scanning.stdin
是從標準輸入讀取的預定義格式化輸入通道),f
定義為 let f x = x + 1
,則 bscanf Scanning.stdin "%d" f
從標準輸入讀取一個整數 n
並返回 f n
(即 n + 1
)。因此,如果我們評估 bscanf stdin "%d" f
,然後在鍵盤上輸入 41
,我們得到的結果是 42
。
OCaml 掃描功能讓人想起 C 語言中對應的功能。但是,它在很大程度上也是不同的,更簡單,而且更強大:格式化輸入函式是高階函數,參數傳遞機制只是常規的函式應用,而不是基於變數賦值的機制,這在命令式語言中的格式化輸入中很典型; OCaml 格式字串還具有有用的新增功能,可以輕鬆定義複雜的標記;正如在函數式程式設計語言中所預期的那樣,格式化輸入函式也支援多型,特別是與多型使用者定義的掃描器進行任意互動。此外,OCaml 格式化輸入功能在編譯時會完全進行類型檢查。
不同步的存取
對 Scanf.Scanning.in_channel
的不同步存取可能會導致無效的 Scanf.Scanning.in_channel
狀態。因此,必須同步對 Scanf.Scanning.in_channel
的並行存取(例如,使用 Mutex.t
)。
module Scanning:sig
..end
type('a, 'b, 'c, 'd)
scanner =('a, Scanning.in_channel, 'b, 'c, 'a -> 'd, 'd) format6 -> 'c
格式化輸入掃描器的類型:('a, 'b, 'c, 'd) scanner
是一種格式化輸入函式的類型,它根據某些格式字串從某些格式化輸入通道讀取;更確切地說,如果 scan
是一些格式化輸入函式,則 scan
會在
ic fmt fscan
從 Scanf.Scanning.in_channel
格式化輸入通道 ic
中讀取這些引數時,將 f
套用到格式字串 fmt
指定的所有引數。
例如,下面的 Scanf.scanf
函式具有類型 ('a, 'b, 'c, 'd) scanner
,因為它是一個從 Scanf.Scanning.stdin
讀取的格式化輸入函式:scanf fmt f
將 f
套用到 fmt
指定的引數,並按照預期從 stdin
讀取這些引數。
如果格式 fmt
具有某些 %r
指示,則必須在接收器函式 f
之前提供對應的格式化輸入函式。例如,如果 read_elem
是類型為 t
的值的輸入函式,則 bscanf ic "%r;" read_elem f
會讀取類型為 t
的值 v
,後跟一個 ';'
字元,並返回 f v
。
type('a, 'b, 'c, 'd)
scanner_opt =('a, Scanning.in_channel, 'b, 'c, 'a -> 'd option, 'd) format6 ->
'c
exception Scan_failure of string
當無法根據格式字串規範讀取輸入時,格式化輸入函式通常會引發例外 Scan_failure
。
val bscanf : Scanning.in_channel -> ('a, 'b, 'c, 'd) scanner
bscanf ic fmt r1 ... rN f
從 Scanf.Scanning.in_channel
格式化輸入通道 ic
讀取字元,並根據格式字串 fmt
將它們轉換為值。作為最後一步,接收器函式 f
會套用到讀取的值,並給出 bscanf
呼叫的結果。
例如,如果 f
是函式 fun s i -> i + 1
,則 Scanf.sscanf "x = 1" "%s = %i" f
返回 2
。
引數 r1
到 rN
是使用者定義的輸入函式,用於讀取與格式字串中指定的 %r
轉換對應的引數。
val bscanf_opt : Scanning.in_channel -> ('a, 'b, 'c, 'd) scanner_opt
與 Scanf.bscanf
相同,但在掃描失敗的情況下返回 None
。
格式字串是一個字元字串,其中包含三種類型的物件:
f
的一個引數(請參閱 格式字串中的轉換規範),如上所述,格式字串中的純文字字元只是與輸入的下一個字元匹配;但是,有兩個字元是此規則的特殊例外:空格字元(' '
或 ASCII 碼 32)和換行符字元('\n'
或 ASCII 碼 10)。空格不匹配單個空格字元,而是匹配輸入中的任意數量的「空白」。更準確地說,格式字串中的空格匹配任意數量的 Tab、空格、換行符和歸位字元。同樣地,格式字串中的換行符字元匹配單個換行符或後接換行符的歸位。
匹配任何數量的空白,格式字串中的空格也完全不匹配任何數量的空白;因此,當讀取包含各種空白的輸入時,例如 Price = 1 $
、Price = 1 $
,甚至 Price=1$
,呼叫 bscanf ib
會成功並返回
"Price = %d $" (fun p -> p)1
。
轉換規範由 %
字元、後跟一個可選的標誌、一個可選的欄位寬度,以及一個或兩個轉換字元組成。
轉換字元及其含義如下:
d
:讀取一個可選帶符號的十進制整數(0-9
+)。i
:讀取一個可選帶符號的整數(理解十進制(0-9
+)、十六進制(0x[0-9a-f]+
和 0X[0-9A-F]+
)、八進制(0o[0-7]+
)和二進制(0b[0-1]+
)符號的常用輸入慣例)。u
:讀取一個無符號十進制整數。x
或 X
:讀取一個無符號十六進制整數([0-9a-fA-F]+
)。o
:讀取一個無符號八進制整數([0-7]+
)。s
:讀取一個盡可能擴展的字串引數,直到以下邊界條件成立:S
:讀取一個定界的字串引數(分隔符號和特殊的跳脫字元遵循 OCaml 的詞彙慣例)。c
: 讀取單一字元。若要在不讀取的情況下測試目前的輸入字元,請指定 null 欄位寬度,即使用規格 %0c
。如果欄位寬度規格大於 1,則會引發 Invalid_argument
例外。C
: 讀取單一分隔字元(分隔符號和特殊跳脫字元遵循 OCaml 的詞彙慣例)。f
、e
、E
、g
、G
: 讀取十進位表示法的可選帶正負號浮點數,格式為 dddd.ddd
e/E+-dd
。h
、H
: 讀取十六進位表示法的可選帶正負號浮點數。F
: 根據 OCaml 的詞彙慣例讀取浮點數(因此,如果未提及指數部分,則小數點是強制性的)。B
: 讀取布林引數 (true
或 false
)。b
: 讀取布林引數(為了向後相容性;請勿在新程式中使用)。ld
、li
、lu
、lx
、lX
、lo
: 讀取一個 int32
引數,格式由第二個字母指定,用於一般整數。nd
、ni
、nu
、nx
、nX
、no
: 讀取一個 nativeint
引數,格式由第二個字母指定,用於一般整數。Ld
、Li
、Lu
、Lx
、LX
、Lo
: 讀取一個 int64
引數,格式由第二個字母指定,用於一般整數。[ range ]
: 讀取符合字元範圍 range
中提及的其中一個字元(或範圍以 ^
開頭時,則不符合範圍)的字元。讀取一個 string
,如果下一個輸入字元不符合範圍,則可以為空。從 c1
到 c2
(含)的字元集合表示為 c1-c2
。因此,%[0-9]
會傳回一個表示十進位數字的字串,如果找不到任何十進位數字,則傳回空字串;同樣地,%[0-9a-f]
會傳回一個十六進位數字的字串。如果右方括號出現在範圍中,則它必須作為範圍的第一個字元出現(或在範圍否定時,緊接在 ^
之後);因此,[]]
會比對一個 ]
字元,而 [^]]
會比對任何不是 ]
的字元。使用 %%
和 %@
在範圍中包含 %
或 @
。r
: 使用者定義的讀取器。取得下一個 ri
格式化的輸入函式,並將其套用至掃描緩衝區 ib
以讀取下一個引數。因此,輸入函式 ri
的類型必須為 Scanning.in_channel -> 'a
,而讀取的引數類型為 'a
。{ fmt %}
: 讀取格式字串引數。讀取的格式字串必須與格式字串規格 fmt
的類型相同。例如,"%{ %i %}"
會讀取任何可以讀取 int
類型值的格式字串;因此,如果 s
是字串 "fmt:\"number is %u\""
,則 Scanf.sscanf s "fmt: %{%i%}"
會成功並傳回格式字串 "number is %u"
。( fmt %)
: 掃描子格式替代。在輸入中讀取格式字串 rf
,然後繼續使用 rf
進行掃描,而不是使用 fmt
進行掃描。格式字串 rf
必須與其取代的格式字串規格 fmt
類型相同。例如,"%( %i %)"
會讀取任何可以讀取 int
類型值的格式字串。轉換會傳回讀取的格式字串 rf
,然後傳回使用 rf
讀取的值。因此,如果 s
是字串 "\"%4d\"1234.00"
,則 Scanf.sscanf s "%(%i%)" (fun fmt i -> fmt, i)
的評估結果為 ("%4d", 1234)
。此行為不僅僅是格式替代,因為轉換會傳回讀取的格式字串作為額外的引數。如果您需要純格式替代,請使用特殊旗標 _
來捨棄多餘的引數:轉換 %_( fmt %)
會讀取格式字串 rf
,然後其行為與格式字串 rf
相同。因此,如果 s
是字串 "\"%4d\"1234.00"
,則 Scanf.sscanf s "%_(%i%)"
相當於 Scanf.sscanf "1234.00" "%4d"
。l
: 傳回目前為止讀取的行數。n
: 傳回目前為止讀取的字元數。N
或 L
: 傳回目前為止讀取的 Token 數。!
: 比對輸入結束條件。%
: 比對輸入中的一個 %
字元。@
: 比對輸入中的一個 @
字元。,
: 不執行任何動作。在引入轉換的 %
字元之後,可能會出現特殊旗標 _
:後續的轉換會照常發生,但產生的值會被捨棄。例如,如果 f
是函式 fun i -> i + 1
,而 s
是字串 "x = 1"
,則 Scanf.sscanf s "%_s = %i" f
會傳回 2
。
欄位寬度由一個可選的整數字面值組成,表示要讀取的 Token 的最大寬度。例如,%6d
會讀取一個最多有 6 個十進位數字的整數;%4f
會讀取一個最多有 4 個字元的浮點數;而 %8[\000-\255]
會傳回接下來的 8 個字元(或如果輸入中少於 8 個字元,則會傳回所有仍可用的字元)。
附註
%s
轉換總是會成功,即使輸入中沒有任何可讀取的內容:在這種情況下,它只會傳回 ""
。'_'
字元也可能出現在數字內(這讓人想起常用的 OCaml 詞彙慣例)。如果需要更嚴格的掃描,請使用範圍轉換機制,而不是數字轉換。scanf
機制不適用於繁重的詞彙分析和剖析。如果它看起來不夠表達您的需求,則有多種替代方案存在:正規表示式(模組 Str
)、串流剖析器、ocamllex
產生的詞彙分析器、ocamlyacc
產生的剖析器。掃描指示出現在字串轉換 %s
和 %[ range ]
之後,以分隔 Token 的結尾。掃描指示由 @
字元引入,後接一些純字元 c
。這表示字串 Token 應該在下一個符合的 c
之前結束(該字元會被跳過)。如果未遇到任何 c
字元,則字串 Token 會盡可能擴展。例如,"%s@\t"
會讀取一個字串,直到下一個 Tab 字元或輸入結尾。如果 @
字元出現在格式字串中的其他任何位置,則會將其視為純字元。
附註
%
和 @
字元必須使用 %%
和 %@
跳脫;此規則仍然適用於範圍規格和掃描指示內。例如,格式 "%s@%%"
會讀取一個字串,直到下一個 %
字元,而格式 "%s@%@"
會讀取一個字串,直到下一個 @
。Printf
模組使用的格式字串相比,掃描指示在 Scanf
格式字串的語法中引入了細微的差異。但是,掃描指示與 Format
模組中使用的指示相似;因此,在產生要由 Scanf.bscanf
掃描的格式化文字時,明智之舉是使用 Format
模組中的列印函式(或者,如果您需要使用 Printf
中的函式,請禁止或仔細檢查包含 '@'
字元的格式字串)。當無法根據格式字串讀取輸入時,掃描器可能會引發下列例外狀況
Scanf.Scan_failure
。Failure
。End_of_file
。Invalid_argument
。附註
%s
轉換永遠不會引發例外狀況 End_of_file
:如果到達輸入結尾,轉換會成功,並只會傳回目前為止讀取的字元,或者如果從未讀取任何字元,則會傳回 ""
。val sscanf : string -> ('a, 'b, 'c, 'd) scanner
與 Scanf.bscanf
相同,但從給定的字串讀取。
val sscanf_opt : string -> ('a, 'b, 'c, 'd) scanner_opt
與 Scanf.sscanf
相同,但在掃描失敗的情況下會傳回 None
。
val scanf : ('a, 'b, 'c, 'd) scanner
與 Scanf.bscanf
相同,但從預定義的格式化輸入通道 Scanf.Scanning.stdin
讀取,該通道連接到 stdin
。
val scanf_opt : ('a, 'b, 'c, 'd) scanner_opt
與 Scanf.scanf
相同,但若掃描失敗則返回 None
。
val kscanf : Scanning.in_channel ->
(Scanning.in_channel -> exn -> 'd) -> ('a, 'b, 'c, 'd) scanner
與 Scanf.bscanf
相同,但額外接受一個函數參數 ef
,該函數會在發生錯誤時被調用:如果掃描過程或某些轉換失敗,掃描函數會中止並調用錯誤處理函數 ef
,並將格式化輸入通道和導致掃描過程中止的例外狀況作為參數傳遞給它。
val ksscanf : string ->
(Scanning.in_channel -> exn -> 'd) -> ('a, 'b, 'c, 'd) scanner
與 Scanf.kscanf
相同,但從給定的字串讀取。
val bscanf_format : Scanning.in_channel ->
('a, 'b, 'c, 'd, 'e, 'f) format6 ->
(('a, 'b, 'c, 'd, 'e, 'f) format6 -> 'g) -> 'g
bscanf_format ic fmt f
根據給定的格式字串 fmt
從格式化輸入通道 ic
讀取格式字串 token,並將 f
應用於產生的格式字串值。
Scan_failure
,如果讀取的格式字串值與 fmt
的類型不相同。val sscanf_format : string ->
('a, 'b, 'c, 'd, 'e, 'f) format6 ->
(('a, 'b, 'c, 'd, 'e, 'f) format6 -> 'g) -> 'g
與 Scanf.bscanf_format
相同,但從給定的字串讀取。
val format_from_string : string ->
('a, 'b, 'c, 'd, 'e, 'f) format6 ->
('a, 'b, 'c, 'd, 'e, 'f) format6
format_from_string s fmt
根據給定的格式字串 fmt
將字串參數轉換為格式字串。
Scan_failure
,如果 s
被視為格式字串,則其類型與 fmt
的類型不相同。val unescaped : string -> string
unescaped s
返回 s
的副本,其中逸出序列(根據 OCaml 的詞法慣例)被它們對應的特殊字符替換。更精確地說,Scanf.unescaped
具有以下特性:對於所有字串 s
,Scanf.unescaped (String.escaped s) = s
。
總是返回參數的副本,即使參數中沒有逸出序列。
Scan_failure
,如果 s
沒有正確逸出(即 s
具有無效的逸出序列或未正確逸出的特殊字符)。例如,Scanf.unescaped "\""
將會失敗。