模組 Format

module Format: sig .. end

美觀列印。

如果您是這個模組的新手,請參閱下方的 範例

此模組實作了一種美觀列印機制,可在 '美觀列印方塊''語意標籤' 中格式化數值,並結合一組 類似 printf 的函式。美觀列印器會在指定的 換行提示處分割行,並根據方塊結構縮排行。類似地,語意標籤可用於將文字呈現與其內容分離。

此美觀列印機制實作為抽象 格式器之上的覆蓋層,格式器提供基本的輸出函式。一些格式器是預先定義的,特別是

Format 模組中的大多數函式都有兩種變體:一種短版本,作用於使用 Format.get_std_formatter 取得的目前網域的標準格式器,以及以 pp_ 為字首的通用版本,將格式器作為其第一個引數。對於作用於目前網域標準格式器的版本,對 Format.get_std_formatter 的呼叫會延遲到收到最後一個引數時。

可以使用 Format.formatter_of_out_channelFormat.formatter_of_bufferFormat.formatter_of_symbolic_output_buffer 或使用 自訂格式器建立更多格式器。

警告:由於 格式器包含可變狀態,因此在沒有同步的情況下,在多個網域上並行使用相同的格式器並不是執行緒安全的。

如果多個網域使用預先定義的格式器(例如透過 Format.get_std_formatterFormat.get_err_formatter 取得的)寫入相同的輸出通道,則網域的輸出將在格式器被清除的位置(例如使用 Format.print_flush)彼此交錯。從 Format.formatter_of_out_channel(在標準輸出通道或其他通道上)取得的格式器不會執行此同步。


簡介

您可以將此模組視為提供 printf 機制的延伸,以提供自動換行。將美觀列印註釋新增到您的常規 printf 格式字串,即可提供精美的縮排和換行。美觀列印註釋在函式 Format.fprintf 的文件中描述如下。

您也可以使用此模組提供的明確美觀列印方塊管理和列印函式。這種樣式比簡潔的 fprintf 格式字串更基本,但也更冗長。

例如,序列 open_box 0; print_string "x ="; print_space ();
    print_int 1; close_box (); print_newline ()
在美觀列印方塊中列印 x = 1,可以縮寫為 printf "@[%s@ %i@]@." "x =" 1,甚至更短的 printf "@[x =@ %i@]@." 1

此函式庫的臨時使用者需遵循的原則

如果沒有開啟的美觀列印方塊,則美觀列印命令的行為是不明確的。由以下 open_ 函式之一開啟的每個方塊都必須使用 close_box 關閉,才能正確格式化。否則,方塊中列印的某些內容可能不會輸出,或者可能格式不正確。

在互動式使用時,每個短語都會在標準美觀列印器的初始狀態下執行:在每個短語執行之後,互動式系統會關閉所有開啟的美觀列印方塊、清除所有擱置的文字,並重設標準美觀列印器。

警告:將此模組的美觀列印函式呼叫與對 Stdlib 低階輸出函式的呼叫混合使用容易出錯。

美觀列印函式輸出的內容會延遲在美觀列印器佇列中,並堆疊以計算正確的換行。相反地,基本 I/O 輸出函式會直接寫入其輸出裝置。因此,基本 I/O 函式的輸出可能會在之前呼叫的美觀列印函式輸出之前出現。例如,
    Stdlib.print_string "<";
    Format.print_string "PRETTY";
    Stdlib.print_string ">";
    Format.print_string "TEXT";
   
會產生輸出 <>PRETTYTEXT

格式器

type formatter 

與美觀列印器(也稱為格式器)及其所有機制對應的抽象資料。另請參閱 定義格式器

美觀列印方塊

美觀列印引擎使用美觀列印方塊和換行提示的概念來驅動美觀列印器的縮排和換行行為。

每個不同的美觀列印方塊種類都會引入特定的換行策略

請注意,換行策略是方塊特定的:方塊的策略不會規則內部方塊的策略。例如,如果垂直方塊巢狀在水平方塊中,則垂直方塊內的所有換行提示都會分割行。

此外,在 最大縮排限制之後開啟方塊會分割行,無論該方塊是否最終符合該行。

val pp_open_box : formatter -> int -> unit
val open_box : int -> unit

pp_open_box ppf d 會在格式器 ppf 中開啟具有偏移量 d 的新壓縮美觀列印方塊。

在此方塊內,美觀列印器會在每一行上盡可能列印內容。

如果該行上沒有更多空間可以列印方塊的其餘部分,則換行提示會分割行。

在此方塊內,美觀列印器會強調方塊結構:如果結構方塊無法完全符合單行,則如果分割「向左移動」(也就是說,新行的縮排小於目前行的縮排),則換行提示也會分割行。

此方塊是一般用途的美觀列印方塊。

如果美觀列印器在方塊中分割行,則會將偏移量 d 新增到目前的縮排。

val pp_close_box : formatter -> unit -> unit
val close_box : unit -> unit

關閉最近開啟的美觀列印方塊。

val pp_open_hbox : formatter -> unit -> unit
val open_hbox : unit -> unit

pp_open_hbox ppf () 會開啟新的「水平」美觀列印方塊。

此方塊會將內容列印在單行上。

水平方塊中的換行提示永遠不會分割行。(換行仍可能發生在巢狀較深的方塊內)。

val pp_open_vbox : formatter -> int -> unit
val open_vbox : int -> unit

pp_open_vbox ppf d 會開啟具有偏移量 d 的新「垂直」美觀列印方塊。

此方塊會在方塊中盡可能多的行上列印內容作為換行提示。

垂直方塊中的每個換行提示都會分割行。

如果美觀列印器在方塊中分割行,則會將 d 新增到目前的縮排。

val pp_open_hvbox : formatter -> int -> unit
val open_hvbox : int -> unit

pp_open_hvbox ppf d 會開啟具有偏移量 d 的新「水平/垂直」美觀列印方塊。

如果此方塊符合單行,則其行為與水平方塊相同,否則其行為與垂直方塊相同。

如果美觀列印器在方塊中分割行,則會將 d 新增到目前的縮排。

val pp_open_hovbox : formatter -> int -> unit
val open_hovbox : int -> unit

pp_open_hovbox ppf d 會開啟具有偏移量 d 的新「水平或垂直」美觀列印方塊。

此方塊會在每一行上盡可能列印內容。

如果該行上沒有更多空間可以列印方塊的其餘部分,則換行提示會分割行。

如果美觀列印器在方塊中分割行,則會將 d 新增到目前的縮排。

格式化函式

val pp_print_string : formatter -> string -> unit
val print_string : string -> unit

pp_print_string ppf s 會在目前的美觀列印方塊中列印 s

val pp_print_bytes : formatter -> bytes -> unit
val print_bytes : bytes -> unit

pp_print_bytes ppf b 會在目前的美觀列印方塊中列印 b

val pp_print_as : formatter -> int -> string -> unit
val print_as : int -> string -> unit

pp_print_as ppf len s 會在目前的美觀列印方塊中列印 s。美觀列印器會將 s 格式化,就像其長度為 len 一樣。

val pp_print_int : formatter -> int -> unit
val print_int : int -> unit

在目前的美觀列印方塊中列印整數。

val pp_print_float : formatter -> float -> unit
val print_float : float -> unit

在目前的美觀列印方塊中列印浮點數。

val pp_print_char : formatter -> char -> unit
val print_char : char -> unit

在目前的美觀列印方塊中列印字元。

val pp_print_bool : formatter -> bool -> unit
val print_bool : bool -> unit

在目前的美觀列印方塊中列印布林值。

val pp_print_nothing : formatter -> unit -> unit

不列印任何內容。

換行提示

「換行提示」會告訴美觀列印器輸出一些空格或分割行,無論哪種方式更適合目前的列印方塊分割規則。

換行提示用於分隔列印項目,並且必須讓美觀列印器正確分割行並縮排項目。

簡單的換行提示包括

注意:對於美化列印引擎而言,空間和換行的概念是抽象的,因為這些概念可以完全由程式設計師重新定義。但是,在美化列印器的預設設定中,「輸出一個空格」僅表示列印一個空格字元(ASCII 代碼 32),而「換行」表示列印一個換行符號(ASCII 代碼 10)。

val pp_print_space : formatter -> unit -> unit
val print_space : unit -> unit

pp_print_space ppf () 會發出一個「空格」斷行提示:美化列印器可能會在此處換行,否則會列印一個空格。

pp_print_space ppf () 等同於 pp_print_break ppf 1 0

val pp_print_cut : formatter -> unit -> unit
val print_cut : unit -> unit

pp_print_cut ppf () 會發出一個「剪切」斷行提示:美化列印器可能會在此處換行,否則不會列印任何內容。

pp_print_cut ppf () 等同於 pp_print_break ppf 0 0

val pp_print_break : formatter -> int -> int -> unit
val print_break : int -> int -> unit

pp_print_break ppf nspaces offset 會發出一個「完整」斷行提示:美化列印器可能會在此處換行,否則會列印 nspaces 個空格。

如果美化列印器換行,則將 offset 加到目前的縮排。

val pp_print_custom_break : formatter ->
fits:string * int * string -> breaks:string * int * string -> unit

pp_print_custom_break ppf ~fits:(s1, n, s2) ~breaks:(s3, m, s4) 會發出一個自訂斷行提示:美化列印器可能會在此處換行。

如果它沒有換行,則會發出 s1,然後是 n 個空格,然後是 s2

如果它換行,則會發出 s3 字串,然後是一個縮排(根據方塊規則),然後是 m 個空格的偏移量,然後是 s4 字串。

雖然 nmformatter_out_functions.out_indent 處理,但字串將由 formatter_out_functions.out_string 處理。這允許自訂格式器以不同的方式處理縮排,例如,輸出 <br/> 標籤或 &nbsp; 實體。

如果您想在換行或不換行時更改列印的可见(非空白)字元,則自訂換行很有用。例如,當列印列表  [a; b; c]  時,您可能希望在垂直列印時新增尾隨分號

[
  a;
  b;
  c;
]
   

您可以按如下方式執行此操作

printf "@[<v 0>[@;<0 2>@[<v 0>a;@,b;@,c@]%t]@]@\n"
  (pp_print_custom_break ~fits:("", 0, "") ~breaks:(";", 0, ""))
   
val pp_force_newline : formatter -> unit -> unit
val force_newline : unit -> unit

在目前的美化列印方塊中強制換行。

美化列印器必須在此處換行。

這不是美化列印的正常方式,因為命令式換行可能會干擾目前的行計數器和方塊大小計算。在封閉的垂直方塊中使用換行提示是更好的選擇。

val pp_print_if_newline : formatter -> unit -> unit
val print_if_newline : unit -> unit

如果前一行剛好被分開,則執行下一個格式化命令。否則,忽略下一個格式化命令。

美觀列印終止

val pp_print_flush : formatter -> unit -> unit
val print_flush : unit -> unit

美化列印結束:將美化列印器重設為初始狀態。

所有開啟的美化列印方塊都會關閉,所有未處理的文字都會列印。此外,美化列印器的低階輸出裝置會被清除,以確保所有未處理的文字都真正顯示出來。

注意:在美化列印常規過程中,切勿使用 print_flush,因為美化列印器使用複雜的緩衝機制來正確縮排輸出;在隨機時間手動清除這些緩衝區會與美化列印器策略衝突,並導致呈現效果不佳。

只有在顯示所有未處理的資料是強制性的情況下(例如,在互動式使用中,您希望使用者閱讀某些文字)以及重設美化列印器狀態不會干擾進一步美化列印時,才考慮使用 print_flush

警告:如果美化列印器的輸出裝置是輸出通道,則重複呼叫 print_flush 表示重複呼叫 flush 以清除輸出通道;這些明確的清除呼叫可能會阻礙輸出通道的緩衝策略,並可能嚴重影響效率。

val pp_print_newline : formatter -> unit -> unit
val print_newline : unit -> unit

美化列印結束:將美化列印器重設為初始狀態。

所有開啟的美化列印方塊都會關閉,所有未處理的文字都會列印。

等同於 Format.print_flush,並且在清除裝置之前立即在美化列印器的低階輸出裝置上發出新行。請參閱 Format.print_flush 的相應警示字詞。

注意:這不是輸出新行的正常方式;首選方法是在垂直美化列印方塊中使用換行提示。

邊界

val pp_infinity : int

pp_infinity 是邊距的最大大小。其確切值取決於實作,但保證大於 109

val pp_set_margin : formatter -> int -> unit
val set_margin : int -> unit

pp_set_margin ppf d 將右邊距設定為 d(以字元為單位):美化列印器會根據給定的換行提示,將超出右邊距的行分開。將邊距設定為 d 表示格式化引擎的目標是每行最多列印 d-1 個字元。如果 d 小於 2,則不會發生任何事。如果 d >= Format.pp_infinity,則右邊距設定為 Format.pp_infinity - 1。如果 d 小於目前的縮排上限,則會降低縮排上限,同時嘗試保留最小比例 max_indent/margin>=50%,並且盡可能保留目前的差異 margin - max_indent

另請參閱 Format.pp_set_geometry

val pp_get_margin : formatter -> unit -> int
val get_margin : unit -> int

傳回右邊距的位置。

最大縮排限制

val pp_set_max_indent : formatter -> int -> unit
val set_max_indent : int -> unit

pp_set_max_indent ppf d 將行的最大縮排上限設定為 d(以字元為單位):一旦達到此上限,新的美化列印方塊將被拒絕到左側,除非封閉的方塊完全符合目前的行。如範例所示,

 set_margin 10; set_max_indent 5; printf "@[123456@[7@]89A@]@." 

產生

    123456
    789A
  

因為巢狀方塊 "@[7@]" 是在最大縮排上限 (7>5) 之後開啟的,並且其父方塊不符合目前的行。您可以減少父方塊的長度以使其符合一行

 printf "@[123456@[7@]89@]@." 

或在符合目前行的最大縮排上限之前開啟一個中間方塊

 printf "@[123@[456@[7@]89@]A@]@." 

避免了將內部方塊拒絕到左側,並分別列印 "123456789""123456789A"。另請注意,垂直方塊永遠不符合一行,而水平方塊始終完全符合目前的行。開啟方塊可能會分開一行,而內容可能已經符合。如果此行為有問題,可以透過將最大縮排上限設定為 margin - 1 來減少此行為。請注意,將最大縮排上限設定為 margin 是無效的。

如果 d 小於 2,則不會發生任何事。

如果 d 大於目前的邊距,則會忽略它,並保留目前的最大縮排上限。

另請參閱 Format.pp_set_geometry

val pp_get_max_indent : formatter -> unit -> int
val get_max_indent : unit -> int

傳回最大縮排上限(以字元為單位)。

幾何

幾何函式可用於同時操作耦合變數,即邊距和最大縮排上限。

type geometry = {
   max_indent : int;
   margin : int;
}
val check_geometry : geometry -> bool

檢查格式器幾何是否有效: 1 < max_indent < margin < Format.pp_infinity

val pp_set_geometry : formatter -> max_indent:int -> margin:int -> unit
val set_geometry : max_indent:int -> margin:int -> unit
val pp_safe_set_geometry : formatter -> max_indent:int -> margin:int -> unit
val safe_set_geometry : max_indent:int -> margin:int -> unit

pp_set_geometry ppf ~max_indent ~margin 設定 ppf 的邊距和最大縮排上限。

1 < max_indent < margin < Format.pp_infinity 時,pp_set_geometry ppf ~max_indent ~margin 等同於 pp_set_margin ppf margin; pp_set_max_indent ppf max_indent;並避免細微不正確的 pp_set_max_indent ppf max_indent; pp_set_margin ppf margin

在此網域之外,pp_set_geometry 會引發無效引數例外,而 pp_safe_set_geometry 則不會執行任何動作。

val pp_update_geometry : formatter -> (geometry -> geometry) -> unit

pp_update_geometry ppf (fun geo -> { geo with ... }) 可讓您以對 geometry 記錄的新欄位擴充具有強大適應性的方式更新格式器的幾何。

如果傳回的幾何不滿足 Format.check_geometry,則會引發無效引數例外。

val update_geometry : (geometry -> geometry) -> unit
val pp_get_geometry : formatter -> unit -> geometry
val get_geometry : unit -> geometry

傳回格式器的目前幾何

最大格式化深度

最大格式化深度是同時開啟的美化列印方塊的最大數目。

較深巢狀方塊內的材料會列印為省略號(更精確地說是 Format.get_ellipsis_text () 傳回的文字)。

val pp_set_max_boxes : formatter -> int -> unit
val set_max_boxes : int -> unit

pp_set_max_boxes ppf max 設定同時開啟的美化列印方塊的最大數目。

較深巢狀方塊內的材料會列印為省略號(更精確地說是 Format.get_ellipsis_text () 傳回的文字)。

如果 max 小於 2,則不會發生任何事。

val pp_get_max_boxes : formatter -> unit -> int
val get_max_boxes : unit -> int

傳回允許省略號之前允許的最大美化列印方塊數。

val pp_over_max_boxes : formatter -> unit -> bool
val over_max_boxes : unit -> bool

測試是否已開啟允許的最大美化列印方塊數。

製表方塊

定位標籤方塊在分為固定長度的儲存格的行上列印材料。定位標籤方塊提供了一種簡單的方法來顯示左對齊文字的垂直欄。

此方塊具有命令 set_tab 來定義儲存格邊界,以及命令 print_tab 來從儲存格移動到儲存格,並且當行中沒有更多儲存格要列印時分割行。

注意:在定位標籤方塊中列印是以行定向的,因此定位標籤方塊內的任意行分割會導致呈現效果不佳。但是,在模組 Format 中控制使用定位標籤方塊允許簡單列印欄。

val pp_open_tbox : formatter -> unit -> unit
val open_tbox : unit -> unit

open_tbox () 開啟新的定位標籤方塊。

此方塊會將行分割為固定寬度的儲存格列印。

在定位標籤方塊內,特殊的定位標籤標記會定義行上的關注點(例如,分隔儲存格邊界)。函式 Format.set_tab 在插入點設定定位標籤標記。

定位標籤方塊具有特定的定位標籤換行,以移動到下一個定位標籤標記或分割行。函式 Format.print_tbreak 會列印定位標籤換行。

val pp_close_tbox : formatter -> unit -> unit
val close_tbox : unit -> unit

關閉最近開啟的定位標籤方塊。

val pp_set_tab : formatter -> unit -> unit
val set_tab : unit -> unit

在目前的插入點設定定位標籤標記。

val pp_print_tab : formatter -> unit -> unit
val print_tab : unit -> unit

print_tab () 會發出「下一個」定位標籤換行提示:如果尚未設定在定位標籤標記上,則插入點會移動到右側的第一個定位標籤標記,或美化列印器分割行,且插入點會移動到最左側的定位標籤標記。

它等同於 print_tbreak 0 0

val pp_print_tbreak : formatter -> int -> int -> unit
val print_tbreak : int -> int -> unit

print_tbreak nspaces offset 會發出一個「完整」的跳格中斷提示。

如果跳格標記上尚未設定,插入點會移至右側第一個跳格標記,並且美化列印器會印出 nspaces 個空格。

如果右側沒有下一個跳格標記,美化列印器會在該點分割行,然後插入點會移至方塊最左邊的跳格標記。

如果美化列印器換行,則將 offset 加到目前的縮排。

省略符號

val pp_set_ellipsis_text : formatter -> string -> unit
val set_ellipsis_text : string -> unit

設定當開啟過多的美化列印方塊時,所列印的省略符號文字(預設為單一點號 .)。

val pp_get_ellipsis_text : formatter -> unit -> string
val get_ellipsis_text : unit -> string

傳回省略符號的文字。

語意標籤

type stag = ..

語意標籤(或簡稱 標籤)是用戶定義的註釋,用於將用戶特定的操作關聯到列印的實體。

語意標籤的常見用法是文字裝飾,以取得顯示裝置的特定字型或文字大小呈現,或標記實體的界限(例如 HTML 或 TeX 元素或終端跳脫序列)。更複雜的語意標籤用法可以處理美化列印器行為的動態修改,以正確列印某些特定標籤內的內容。例如,我們可以這樣定義一個 RGB 標籤

type stag += RGB of {r:int;g:int;b:int}

為了正確界定列印的實體,語意標籤必須在實體之前開啟,在實體之後關閉。語意標籤必須像使用 Format.pp_open_stagFormat.pp_close_stag 的括號一樣正確巢狀排列。

標籤特定操作會在每次開啟或關閉標籤時發生。每次發生時,都會執行兩種操作:標籤標記標籤列印

  • 標籤標記操作是較簡單的標籤特定操作:它只是將標籤特定的字串寫入格式化器的輸出裝置。標籤標記不會干擾行分割計算。
  • 標籤列印操作是更複雜的標籤特定操作:它可以將任意內容列印到格式化器。標籤列印與目前的美化列印器操作緊密相關。

粗略地說,標籤標記通常用於在呈現裝置中獲得更好的文字呈現,而標籤列印允許微調列印常式,根據語意標籤以不同的方式列印相同的實體(即列印額外內容,甚至省略部分輸出)。

更精確地說:當開啟或關閉語意標籤時,會連續發生「標籤列印」和「標籤標記」操作

  • 列印語意標籤表示使用標籤名稱作為引數呼叫格式化器的特定函式 print_open_stag(或 print_close_stag):然後該標籤列印函式可以將任何常規內容列印到格式化器(以便此內容像往常一樣在格式化器佇列中排隊,以進行進一步的行分割計算)。
  • 標記語意標籤表示使用標籤名稱作為引數呼叫格式化器的特定函式 mark_open_stag(或 mark_close_stag):然後該標籤標記函式可以傳回「標籤開啟標記」(或「標籤關閉標記」),以便直接輸出到格式化器的輸出裝置。

由於直接寫入格式化器的輸出裝置,因此語意標籤標記字串不被視為驅動行分割的列印內容的一部分(換句話說,對行分割而言,對應於標籤標記的字串長度視為零)。

因此,語意標籤處理在某種意義上對於美化列印是透明的,並且不會干擾正常的縮排。因此,單一美化列印常式可以根據標籤的處理方式,輸出簡單的「逐字」內容或更豐富的裝飾輸出。預設情況下,標籤不啟用,因此輸出不會使用標籤資訊進行裝飾。一旦將 set_tags 設定為 true,美化列印器引擎就會遵循標籤並相應地裝飾輸出。

預設標籤標記函式的行為與 HTML 相同:字串標籤會以 "<" 和 ">" 包圍,而其他標籤會被忽略;因此,標籤字串 "t" 的開啟標記是 "<t>",而關閉標記是 "</t>"

預設標籤列印函式不會執行任何操作。

標籤標記和標籤列印函式是使用者可定義的,可以透過呼叫 Format.set_formatter_stag_functions 來設定。

可以使用 Format.set_tags 開啟或關閉語意標籤操作。可以使用 Format.set_mark_tags 開啟或關閉標籤標記操作。可以使用 Format.set_print_tags 開啟或關閉標籤列印操作。

type tag = string 
type stag += 
| String_tag of tag (*

String_tag s 是一個字串標籤 s。字串標籤可以透過明確使用建構函式 String_tag 或使用專用格式語法 "@{<s> ... @}" 來插入。

  • 因為 4.08
*)
val pp_open_stag : formatter -> stag -> unit
val open_stag : stag -> unit

pp_open_stag ppf t 開啟名為 t 的語意標籤。

格式化器的 print_open_stag 標籤列印函式會以 t 作為引數呼叫;然後,mark_open_stag t 提供的 t 的開啟標籤標記會寫入格式化器的輸出裝置。

val pp_close_stag : formatter -> unit -> unit
val close_stag : unit -> unit

pp_close_stag ppf () 關閉最近開啟的語意標籤 t

mark_close_stag t 提供的關閉標籤標記會寫入格式化器的輸出裝置;然後,格式化器的 print_close_stag 標籤列印函式會以 t 作為引數呼叫。

val pp_set_tags : formatter -> bool -> unit
val set_tags : bool -> unit

pp_set_tags ppf b 開啟或關閉語意標籤的處理(預設為關閉)。

val pp_set_print_tags : formatter -> bool -> unit
val set_print_tags : bool -> unit

pp_set_print_tags ppf b 開啟或關閉標籤列印操作。

val pp_set_mark_tags : formatter -> bool -> unit
val set_mark_tags : bool -> unit

pp_set_mark_tags ppf b 開啟或關閉標籤標記操作。

val pp_get_print_tags : formatter -> unit -> bool
val get_print_tags : unit -> bool

傳回標籤列印操作的目前狀態。

val pp_get_mark_tags : formatter -> unit -> bool
val get_mark_tags : unit -> bool

傳回標籤標記操作的目前狀態。

val pp_set_formatter_out_channel : formatter -> out_channel -> unit

重新導向標準格式器輸出

val set_formatter_out_channel : out_channel -> unit

將標準美化列印器輸出重新導向至給定的通道。(標準格式化器的所有輸出函式都會設定為列印到給定通道的預設輸出函式。)

set_formatter_out_channel 等同於 Format.pp_set_formatter_out_channel std_formatter

val pp_set_formatter_output_functions : formatter -> (string -> int -> int -> unit) -> (unit -> unit) -> unit
val set_formatter_output_functions : (string -> int -> int -> unit) -> (unit -> unit) -> unit

pp_set_formatter_output_functions ppf out flush 將標準美化列印器的輸出函式重新導向至函式 outflush

out 函式會執行所有美化列印器的字串輸出。它會使用字串 s、起始位置 p 和字元數 n 來呼叫;它應該輸出 s 的字元 pp + n - 1

每當美化列印器被刷新時(透過轉換 %!,或美化列印指示 @?@.,或使用低階函式 print_flushprint_newline)都會呼叫 flush 函式。

val pp_get_formatter_output_functions : formatter -> unit -> (string -> int -> int -> unit) * (unit -> unit)
val get_formatter_output_functions : unit -> (string -> int -> int -> unit) * (unit -> unit)

傳回標準美化列印器的目前輸出函式。

重新定義格式器輸出

Format 模組功能非常強大,可讓您完全重新定義美化列印輸出的意義:您可以提供自己的函式來定義如何處理縮排、行分割,甚至是必須列印的所有字元的列印!

重新定義輸出函式

type formatter_out_functions = {
   out_string : string -> int -> int -> unit;
   out_flush : unit -> unit;
   out_newline : unit -> unit;
   out_spaces : int -> unit;
   out_indent : int -> unit; (*
  • 因為 4.06
*)
}

特定於格式化器的輸出函式集

  • out_string 函式會執行所有美化列印器的字串輸出。它會使用字串 s、起始位置 p 和字元數 n 來呼叫;它應該輸出 s 的字元 pp + n - 1
  • out_flush 函式會刷新美化列印器輸出裝置。
  • 當美化列印器分割行時,會呼叫 out_newline 來開啟新行。
  • 當分行提示導致空格而不是行分割時,out_spaces 函式會輸出空格。它會使用要輸出的空格數來呼叫。
  • 當美化列印器分割行時,out_indent 函式會執行新行縮排。它會使用新行的縮排值來呼叫。

預設情況下

  • 欄位 out_stringout_flush 是特定於輸出裝置的;(例如,output_stringflush 用於 out_channel 裝置,或 Buffer.add_substringignore 用於 Buffer.t 輸出裝置),
  • 欄位 out_newline 等同於 out_string "\n" 0 1
  • 欄位 out_spacesout_indent 等同於 out_string (String.make n ' ') 0 n
val pp_set_formatter_out_functions : formatter -> formatter_out_functions -> unit
val set_formatter_out_functions : formatter_out_functions -> unit

pp_set_formatter_out_functions ppf out_funsppf 的所有美化列印器輸出函式設定為引數 out_funs 的函式,

這樣,您可以變更縮排的意義(可以是除了僅列印空格字元以外的其他內容)和開啟新行的意義(可以連接到應用程式手邊需要的任何其他動作)。

函式 out_spacesout_newline 的合理預設值分別為 out_funs.out_string (String.make n ' ') 0 nout_funs.out_string "\n" 0 1

val pp_get_formatter_out_functions : formatter -> unit -> formatter_out_functions
val get_formatter_out_functions : unit -> formatter_out_functions

傳回美化列印器的目前輸出函式,包括行分割和縮排函式。用於記錄目前設定並在之後還原它。

重新定義語意標籤操作

type formatter_stag_functions = {
   mark_open_stag : stag -> string;
   mark_close_stag : stag -> string;
   print_open_stag : stag -> unit;
   print_close_stag : stag -> unit;
}

格式器特定的語義標籤處理函數:mark 版本是「標籤標記」函數,它將字串標記與標籤關聯,以便美化列印引擎將這些標記作為長度為 0 的符號寫入格式器的輸出裝置。print 版本是「標籤列印」函數,可以在標籤關閉或開啟時執行常規列印。

val pp_set_formatter_stag_functions : formatter -> formatter_stag_functions -> unit
val set_formatter_stag_functions : formatter_stag_functions -> unit

pp_set_formatter_stag_functions ppf tag_funs 變更開啟和關閉語義標籤操作的含義,以便在 ppf 上列印時使用 tag_funs 中的函數。

當開啟名稱為 t 的語義標籤時,字串 t 會傳遞給開啟標籤標記函數(tag_funs 記錄的 mark_open_stag 欄位),該函數必須傳回該名稱的開啟標籤標記。當下一次呼叫 close_stag () 時,語義標籤名稱 t 會傳回給關閉標籤標記函數(tag_funs 記錄的 mark_close_stag 欄位),該函數必須傳回該名稱的關閉標籤標記。

記錄的 print_ 欄位包含標籤列印函數,這些函數在標籤開啟和標籤關閉時呼叫,以便在美化列印佇列中輸出常規資料。

val pp_get_formatter_stag_functions : formatter -> unit -> formatter_stag_functions
val get_formatter_stag_functions : unit -> formatter_stag_functions

傳回標準美化列印器的目前語義標籤操作函數。

定義格式器

定義新的格式器允許在多個輸出裝置上並行輸出不相關的資料。格式器的所有參數都是格式器本機的:右邊距、最大縮排限制、同時開啟的美化列印方塊的最大數量、省略符號等等,都專屬於每個格式器,並且可以獨立固定。

例如,假設有一個 Buffer.t 緩衝區 bFormat.formatter_of_buffer b 會傳回一個新的格式器,使用緩衝區 b 作為其輸出裝置。類似地,假設有一個 out_channel 輸出通道 ocFormat.formatter_of_out_channel oc 會傳回一個新的格式器,使用通道 oc 作為其輸出裝置。

或者,假設給定 out_funs,格式器的一組完整輸出函數,則 Format.formatter_of_out_functions out_funs 會使用這些函數進行輸出,計算出一個新的格式器。

val formatter_of_out_channel : out_channel -> formatter

formatter_of_out_channel oc 會傳回一個新的格式器,該格式器會寫入對應的輸出通道 oc

val synchronized_formatter_of_out_channel : out_channel -> formatter Domain.DLS.key

synchronized_formatter_of_out_channel oc 會傳回域本機狀態的索引鍵,該狀態會保留寫入對應輸出通道 oc 的域本機格式器。

當格式器與多個域一起使用時,來自域的輸出將在格式器刷新的點上相互交錯,例如使用 Format.print_flush 時。

val std_formatter : formatter

初始域的標準格式器,用於寫入標準輸出。

其定義為 Format.formatter_of_out_channel stdout

val get_std_formatter : unit -> formatter

get_std_formatter () 會傳回目前域的標準格式器,該格式器用於寫入標準輸出。

val err_formatter : formatter

初始域的格式器,用於寫入標準錯誤。

其定義為 Format.formatter_of_out_channel stderr

val get_err_formatter : unit -> formatter

get_err_formatter () 會傳回目前域的格式器,該格式器用於寫入標準錯誤。

val formatter_of_buffer : Buffer.t -> formatter

formatter_of_buffer b 會傳回一個新的格式器,該格式器會寫入緩衝區 b。在美化列印結束時,必須使用 Format.pp_print_flushFormat.pp_print_newline 刷新格式器,才能將所有擱置的資料列印到緩衝區中。

val stdbuf : Buffer.t

初始域的字串緩衝區,其中 str_formatter 會寫入。

val get_stdbuf : unit -> Buffer.t

get_stdbuf () 會傳回目前域的字串緩衝區,其中目前域的字串格式器會寫入。

val str_formatter : formatter

初始域的格式器,用於輸出到 Format.stdbuf 字串緩衝區。

str_formatter 定義為 Format.formatter_of_buffer Format.stdbuf

val get_str_formatter : unit -> formatter

目前域的格式器,用於輸出到目前域的字串緩衝區。

val flush_str_formatter : unit -> string

傳回使用目前域的 str_formatter 列印的資料,刷新格式器並重設對應的緩衝區。

val make_formatter : (string -> int -> int -> unit) -> (unit -> unit) -> formatter

make_formatter out flush 會傳回一個新的格式器,該格式器會使用函數 out 進行輸出,並使用函數 flush 進行刷新。

例如,

    make_formatter
      (Stdlib.output_substring oc)
      (fun () -> Stdlib.flush oc)
  

傳回寫入 out_channel oc 的格式器。

val make_synchronized_formatter : (string -> int -> int -> unit) ->
(unit -> unit) -> formatter Domain.DLS.key

make_synchronized_formatter out flush 會傳回域本機狀態的索引鍵,該狀態會保留域本機格式器,該格式器會使用函數 out 進行輸出,並使用函數 flush 進行刷新。

當格式器與多個域一起使用時,來自域的輸出將在格式器刷新的點上相互交錯,例如使用 Format.print_flush 時。

val formatter_of_out_functions : formatter_out_functions -> formatter

formatter_of_out_functions out_funs 會傳回一個新的格式器,該格式器會使用一組輸出函數 out_funs 進行寫入。

如需引數 out_funs 的含義,請參閱類型 Format.formatter_out_functions 的定義。

符號美觀列印

符號美化列印是使用符號格式器進行的美化列印,也就是說,格式器會輸出符號美化列印項目。

當使用符號格式器時,所有常規美化列印活動都會發生,但輸出資料是符號的,並且儲存在輸出項目的緩衝區中。在美化列印結束時,刷新輸出緩衝區允許在執行低階輸出操作之前,對符號輸出進行後處理。

實際上,首先使用

定義符號格式器。像平常一樣使用符號格式器 ppf,並在美化列印結束時,透過使用

type symbolic_output_item = 
| 刷新符號輸出緩衝區 sob 來擷取符號項目。 (*

Output_flush

*)
| 符號刷新命令 (*

Output_newline

*)
| 符號換行命令 (*

Output_string of string

*)
| Output_string s:字串 s 的符號輸出 (*

Output_spaces of int

*)
| Output_spaces n:輸出 n 個空格的符號命令 (*

Output_indent of int

*)

Output_indent i:大小為 i 的符號縮排

type symbolic_output_buffer 

符號美化列印器產生的項目

val make_symbolic_output_buffer : unit -> symbolic_output_buffer

符號美化列印器的輸出緩衝區。

val clear_symbolic_output_buffer : symbolic_output_buffer -> unit

make_symbolic_output_buffer () 會傳回新的符號輸出緩衝區。

val get_symbolic_output_buffer : symbolic_output_buffer -> symbolic_output_item list

clear_symbolic_output_buffer sob 會重設緩衝區 sob

val flush_symbolic_output_buffer : symbolic_output_buffer -> symbolic_output_item list

get_symbolic_output_buffer sob 會傳回緩衝區 sob 的內容。

val add_symbolic_output_item : symbolic_output_buffer -> symbolic_output_item -> unit

flush_symbolic_output_buffer sob 會傳回緩衝區 sob 的內容,並重設緩衝區 sobflush_symbolic_output_buffer sob 等同於 let items = get_symbolic_output_buffer sob in
   clear_symbolic_output_buffer sob; items

val formatter_of_symbolic_output_buffer : symbolic_output_buffer -> formatter

add_symbolic_output_item sob itm 會將項目 itm 新增至緩衝區 sob

便利的格式化函式。

val pp_print_iter : ?pp_sep:(formatter -> unit -> unit) ->
(('a -> unit) -> 'b -> unit) ->
(formatter -> 'a -> unit) -> formatter -> 'b -> unit

formatter_of_symbolic_output_buffer sob 會傳回將輸出寫入 symbolic_output_buffer sob 的符號格式器。

val pp_print_list : ?pp_sep:(formatter -> unit -> unit) ->
(formatter -> 'a -> unit) -> formatter -> 'a list -> unit

pp_print_iter ~pp_sep iter pp_v ppf v 會使用 pp_vppf 上格式化 iter 對值集合 v 的反覆運算。反覆運算會以 pp_sep 分隔(預設為 Format.pp_print_cut)。

val pp_print_array : ?pp_sep:(formatter -> unit -> unit) ->
(formatter -> 'a -> unit) -> formatter -> 'a array -> unit

pp_print_list ?pp_sep pp_v ppf l 會列印清單 l 的項目,使用 pp_v 列印每個項目,並在項目之間呼叫 pp_seppp_sep 預設為 Format.pp_print_cut)。在空清單上不做任何動作。

pp_print_array ?pp_sep pp_v ppf a 會列印陣列 a 的項目,使用 pp_v 列印每個項目,並在項目之間呼叫 pp_seppp_sep 預設為 Format.pp_print_cut)。在空陣列上不做任何動作。

val pp_print_seq : ?pp_sep:(formatter -> unit -> unit) ->
(formatter -> 'a -> unit) ->
formatter -> 'a Seq.t -> unit

如果在呼叫 pp_print_array 後修改了 a,列印的值可能不是預期的值,因為 Format 可以延遲列印。可以透過刷新 ppf 來避免這種情況。

pp_print_seq ?pp_sep pp_v ppf s 會列印序列 s 的項目,使用 pp_v 列印每個項目,並在項目之間呼叫 pp_seppp_sep 預設為 Format.pp_print_cut)。在空序列上不做任何動作。

val pp_print_text : formatter -> string -> unit

此函數不會在無限序列上終止。

val pp_print_option : ?none:(formatter -> unit -> unit) ->
(formatter -> 'a -> unit) -> formatter -> 'a option -> unit

pp_print_text ppf s 會列印 s,並分別使用 Format.pp_print_spaceFormat.pp_force_newline 列印空格和換行符號。

val pp_print_result : ok:(formatter -> 'a -> unit) ->
error:(formatter -> 'e -> unit) ->
formatter -> ('a, 'e) result -> unit

pp_print_result ~ok ~error ppf r 會在 ppf 上印出 r,如果 rOk _,則使用 ok,如果 rError _,則使用 error

val pp_print_either : left:(formatter -> 'a -> unit) ->
right:(formatter -> 'b -> unit) ->
formatter -> ('a, 'b) Either.t -> unit

pp_print_either ~left ~right ppf e 會在 ppf 上印出 e,如果 eEither.Left _,則使用 left,如果 eEither.Right _,則使用 right

格式化的美觀列印

模組 Format 提供了一整套類似 printf 的函數,用於使用格式字串規範進行美化列印。

可以在格式字串中新增特定的註解,以向美化列印引擎發出美化列印命令。

這些註解在格式字串中使用 @ 字元引入。例如, 表示空格分隔符,@, 表示斷點,@[ 開啟一個新的方塊,而 @] 關閉最後一個開啟的方塊。

val fprintf : formatter -> ('a, formatter, unit) format -> 'a

fprintf ff fmt arg1 ... argN 根據格式字串 fmt 格式化引數 arg1argN,並將結果字串輸出到格式化器 ff

格式字串 fmt 是一個字元字串,其中包含三種類型的物件:純文字字元和 Printf 模組中指定的轉換規範,以及 Format 模組特有的美化列印指示。

美化列印指示字元以 @ 字元引入,其含義如下:

注意:為了防止將 @ 字元解釋為美化列印指示,請使用 % 字元將其跳脫。舊的引號模式 @@ 已被棄用,因為它與字元 '@' 的格式化輸入解釋不相容。

範例:printf "@[%s@ %d@]@." "x =" 1 等效於 open_box (); print_string "x ="; print_space ();
   print_int 1; close_box (); print_newline ()
。它在美化列印的「水平或垂直」方塊內列印 x = 1

val printf : ('a, formatter, unit) format -> 'a

與上面的 fprintf 相同,但輸出到 get_std_formatter ()

它與 fun fmt -> fprintf (get_std_formatter ()) fmt 的定義類似,但會延遲呼叫 get_std_formatter,直到收到 format 所需的最後一個引數。當與多個域一起使用時,來自這些域的輸出將在格式化器被刷新的點(例如使用 Format.print_flush 時)相互交錯。

val eprintf : ('a, formatter, unit) format -> 'a

與上面的 fprintf 相同,但輸出到 get_err_formatter ()

它與 fun fmt -> fprintf (get_err_formatter ()) fmt 的定義類似,但會延遲呼叫 get_err_formatter,直到收到 format 所需的最後一個引數。當與多個域一起使用時,來自這些域的輸出將在格式化器被刷新的點(例如使用 Format.print_flush 時)相互交錯。

val sprintf : ('a, unit, string) format -> 'a

與上面的 printf 相同,但不是列印到格式化器,而是傳回一個包含引數格式化結果的字串。請注意,美化列印器佇列會在每次呼叫 sprintf 結束時刷新。請注意,如果您的格式字串包含 %a,您應該使用 asprintf

如果在單個字串上輸出資料的多個相關呼叫 sprintf 的情況下,您應該考慮使用帶有預定義格式化器 str_formatterfprintf,並呼叫 flush_str_formatter () 以取得最終結果。

或者,您可以使用 Format.fprintf 與一個寫入您自己的緩衝區的格式化器:在美化列印結束時刷新格式化器和緩衝區會傳回所需的字串。

val asprintf : ('a, formatter, unit, string) format4 -> 'a

與上面的 printf 相同,但不是列印到格式化器,而是傳回一個包含引數格式化結果的字串。asprintf 的類型非常通用,可以很好地與 %a 轉換互動。

val dprintf : ('a, formatter, unit, formatter -> unit) format4 -> 'a

Format.fprintf 相同,只是格式化器是最後一個引數。dprintf "..." a b c 是一個類型為 formatter -> unit 的函數,可以將其提供給格式規範符 %t

這可以用來替代 Format.asprintf 以延遲格式化決策。在格式化上下文中使用 Format.asprintf 傳回的字串會強制隔離進行格式化決策,並可能過早建立最終字串。Format.dprintf 允許延遲格式化決策,直到知道最終的格式化上下文。例如

  let t = Format.dprintf "%i@ %i@ %i" 1 2 3 in
  ...
  Format.printf "@[<v>%t@]" t
val ifprintf : formatter -> ('a, formatter, unit) format -> 'a

與上面的 fprintf 相同,但不列印任何內容。在有條件列印時忽略某些資料很有用。

使用續體的格式化美化列印。

val kfprintf : (formatter -> 'a) ->
formatter -> ('b, formatter, unit, 'a) format4 -> 'b

與上面的 fprintf 相同,但不會立即傳回,而是在列印結束時將格式化器傳遞給其第一個引數。

val kdprintf : ((formatter -> unit) -> 'a) ->
('b, formatter, unit, 'a) format4 -> 'b

與上面的 Format.dprintf 相同,但不會立即傳回,而是在列印結束時將暫停的列印器傳遞給其第一個引數。

val ikfprintf : (formatter -> 'a) ->
formatter -> ('b, formatter, unit, 'a) format4 -> 'b

與上面的 kfprintf 相同,但不列印任何內容。在有條件列印時忽略某些資料很有用。

val ksprintf : (string -> 'a) -> ('b, unit, string, 'a) format4 -> 'b

與上面的 sprintf 相同,但不是傳回字串,而是將字串傳遞給第一個引數。

val kasprintf : (string -> 'a) -> ('b, formatter, unit, 'a) format4 -> 'b

與上面的 asprintf 相同,但不是傳回字串,而是將字串傳遞給第一個引數。

範例

一些熱身範例,以了解如何使用 Format。

我們有一個配對 (int * bool) 的列表 l,頂層會為我們列印出來

# let l = List.init 20 (fun n -> n, n mod 2 = 0)
  val l : (int * bool) list =
  [(0, true); (1, false); (2, true); (3, false); (4, true); (5, false);
   (6, true); (7, false); (8, true); (9, false); (10, true); (11, false);
   (12, true); (13, false); (14, true); (15, false); (16, true); (17, false);
   (18, true); (19, false)]
 

如果我們想自己列印,而不需要頂層的魔法,我們可以嘗試這樣做

  # let pp_pair out (x,y) = Format.fprintf out "(%d, %b)" x y
  val pp_pair : Format.formatter -> int * bool -> unit = <fun>
  # Format.printf "l: [@[<hov>%a@]]@."
    Format.(pp_print_list ~pp_sep:(fun out () -> fprintf out ";@ ") pp_pair) l
    l: [(0, true); (1, false); (2, true); (3, false); (4, true); (5, false);
        (6, true); (7, false); (8, true); (9, false); (10, true); (11, false);
        (12, true); (13, false); (14, true); (15, false); (16, true);
        (17, false); (18, true); (19, false)]

  

簡而言之,這樣做是

如果我們省略 "@ ",我們會得到一個醜陋的單行印出結果

# Format.printf "l: [@[<hov>%a@]]@."
      Format.(pp_print_list ~pp_sep:(fun out () -> fprintf out "; ") pp_pair) l
  l: [(0, true); (1, false); (2, true); (* ... *); (18, true); (19, false)]
- : unit = ()
    

一般來說,為你的程式中的重要類型定義自訂印表機是一個好習慣。例如,如果你要定義像這樣的基本幾何類型

  type point = {
    x: float;
    y: float;
  }

  type rectangle = {
    ll: point; (* lower left *)
    ur: point; (* upper right *)
  }
  

為了除錯目的,或在日誌或主控台上顯示資訊,為這些類型定義印表機將會很方便。以下是如何做到的一個例子。請注意,"%.3f" 是一個 float 印表機,在小數點後保留 3 位數的精度;"%f" 將會印出所有需要的位數,這有點冗長;"%h" 是一個十六進位的浮點數印表機。

  let pp_point out (p:point) =
    Format.fprintf out "{ @[x=%.3f;@ y=%.3f@] }" p.x p.y

  let pp_rectangle out (r:rectangle) =
    Format.fprintf out "{ @[ll=%a;@ ur=%a@] }"
      pp_point r.ll pp_point r.ur
  

.mli 檔案中,我們可以有

    val pp_point : Format.formatter -> point -> unit

    val pp_rectangle : Format.formatter -> rectangle -> unit
  

這些印表機現在可以在其他印表機中使用 "%a"。

 # Format.printf "some rectangle: %a@."
        (Format.pp_print_option pp_rectangle)
        (Some {ll={x=1.; y=2.}; ur={x=42.; y=500.12345}})
  some rectangle: { l={ x=1.000; y=2.000 }; ur={ x=42.000; y=500.123 } }

  # Format.printf "no rectangle: %a@."
        (Format.pp_option pp_rectangle)
        None
  no rectangle:
  

看看我們如何結合 pp_print_option (option 印表機) 和我們新定義的矩形印表機,就像我們之前使用 pp_print_list 一樣。

如需更詳細的教學,請參閱 "使用 Format 模組"

最後一點:Format 模組是一個起點。OCaml 生態系統擁有許多函式庫,可以讓格式化更容易且更具表達力,並提供更多的組合器、更簡潔的名稱等等。這樣的一個函式庫的例子是 Fmt

也可以使用 https://github.com/ocaml-ppx/ppx_deriving 或類似的 ppx deriver 從類型定義自動衍生出漂亮的印表機。