模組 Stdlib.Format

module Format: Format

簡介

您可以將此模組視為提供 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 Format.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 作為引數呼叫;然後,會將 t 的開啟標籤標記(由 mark_open_stag t 提供)寫入格式器的輸出裝置。

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

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

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

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

flush 函數會在美化列印器排清時呼叫(透過轉換 %!、或美化列印指示 @?@.,或使用低階函數 print_flushprint_newline)。

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_funsmark_open_stag 欄位),該函式必須傳回該名稱的開啟標籤標記。當下一次呼叫 close_stag () 時,語義標籤名稱 t 會傳回給關閉標籤標記函式(記錄 tag_funsmark_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 類型的定義。

符號美觀列印

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

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

實際上,首先使用以下命令定義符號輸出緩衝區 b

像往常一樣使用符號格式化器 ppf,並在美觀列印結束時,透過使用以下命令清除符號輸出緩衝區 sob 來擷取符號項目

type symbolic_output_item = 
| 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

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

val clear_symbolic_output_buffer : symbolic_output_buffer -> unit

clear_symbolic_output_buffer sob 會重設緩衝區 sob

val get_symbolic_output_buffer : symbolic_output_buffer -> symbolic_output_item list

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

val flush_symbolic_output_buffer : symbolic_output_buffer -> symbolic_output_item list

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 add_symbolic_output_item : symbolic_output_buffer -> symbolic_output_item -> unit

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

val formatter_of_symbolic_output_buffer : symbolic_output_buffer -> formatter

formatter_of_symbolic_output_buffer sob 會傳回一個符號格式化器,該格式化器會輸出到 symbolic_output_buffer sob

方便的格式化函式。

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

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

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

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

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

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

如果在呼叫 pp_print_array 後修改了 a,則列印的值可能與預期的不同,因為 Format 可以延遲列印。這可以透過清除 ppf 來避免。

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

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

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

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

pp_print_option ?none pp_v ppf o 會在 ppf 上使用 pp_v 列印 o(如果 oSome v),如果它是 None,則列印 nonenone 預設不會列印任何內容。

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 上使用 ok(如果 rOk _)和 error(如果 rError _)來列印 r

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 上使用 left(如果 eEither.Left _)和 right(如果 eEither.Right _)來列印 e

格式化的美觀列印

模組 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 以在單一字串上輸出資料,則應考慮將 fprintf 與預先定義的格式器 str_formatter 一起使用,並呼叫 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(選項列印器)與我們新定義的矩形列印器結合使用,就像我們之前對 pp_print_list 所做的一樣。

如需更廣泛的教學課程,請參閱 「使用 Format 模組」

最後一個注意事項: Format 模組是一個起點。OCaml 生態系統具有使格式化更容易和更具表現力的程式庫,具有更多的組合器、更簡潔的名稱等。此類程式庫的一個範例是 Fmt

也可以使用 https://github.com/ocaml-ppx/ppx_deriving 或類似的 ppx 衍生器,從類型定義自動衍生美化列印器。