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_box 0
获得的);print_cut ()
输出简单的断行提示,或 print_space ()
输出表示断行提示的空格;print_int
和 print_string
)显示其内容;close_box ()
关闭该框;print_newline ()
。如果没有打开的漂亮打印框,则漂亮打印命令的行为未定义。下面 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
字符串。
虽然 n
和 m
由 formatter_out_functions.out_indent
处理,但字符串将由 formatter_out_functions.out_string
处理。这允许自定义格式化程序以不同的方式处理缩进,例如,输出 <br/>
标记或
实体。
如果您想更改在断行或不断行的情况下打印哪些可见(非空格)字符,则自定义断行非常有用。例如,当打印列表 [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
。
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
大于当前边距,则忽略它,并保留当前最大缩进限制。
val pp_get_max_indent : formatter -> unit -> int
val get_max_indent : unit -> int
返回最大缩进限制(以字符为单位)。
几何函数可用于同时操作耦合变量,即边距和最大缩进限制。
type
geometry = {
|
max_indent : |
|
margin : |
}
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_stag
和Format.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
打开或关闭标签打印操作。
typetag =
string
type
Format.stag +=
| |
String_tag of |
(* |
| *) |
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
作为参数被调用。
formatter -> bool -> unit
: bool -> unit
: pp_set_tags ppf b
打开或关闭语义标签的处理(默认为关闭)。
formatter -> bool -> unit
: bool -> unit
: pp_set_print_tags ppf b
打开或关闭标签打印操作。
formatter -> bool -> unit
: bool -> unit
: pp_set_mark_tags ppf b
打开或关闭标签标记操作。
formatter -> unit -> bool
: unit -> bool
: 返回标签打印操作的当前状态。
formatter -> unit -> bool
: 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
将标准美化打印输出函数重定向到函数 out
和 flush
。
函数 out
执行所有美化打印字符串输出。它以字符串 s
、起始位置 p
和字符数 n
作为参数;它应该输出 s
的 p
到 p + n - 1
之间的字符。
每当美化打印被刷新时(通过转换 %!
或美化打印指示 @?
或 @.
,或使用低级函数 print_flush
或 print_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 : |
|||
|
out_flush : |
|||
|
out_newline : |
|||
|
out_spaces : |
|||
|
out_indent : |
(* |
| *) |
}
特定于格式化程序的一组输出函数
out_string
执行所有美化打印字符串输出。它以字符串 s
、起始位置 p
和字符数 n
作为参数;它应该输出 s
的 p
到 p + n - 1
之间的字符。out_flush
刷新美化打印输出设备。out_newline
在美化打印换行时被调用以打开新行。out_spaces
输出空格。它以要输出的空格数作为参数。out_indent
执行新行缩进。它以新行的缩进值作为参数。默认情况下
out_string
和 out_flush
是特定于输出设备的;(例如,对于 out_channel
设备,是 output_string
和 flush
,或者对于 Buffer.t
输出设备,是 Buffer.add_substring
和 ignore
),out_newline
等效于 out_string "\n" 0 1
;out_spaces
和 out_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_funs
将 ppf
的所有美化打印输出函数设置为参数 out_funs
的函数,
这样,您可以更改缩进的含义(可以是除了打印空格字符之外的其他内容)以及换行符打开的含义(可以连接到应用程序当前所需的任何其他操作)。
函数 out_spaces
和 out_newline
的合理默认值分别是 out_funs.out_string (String.make n ' ') 0 n
和 out_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 : |
|
mark_close_stag : |
|
print_open_stag : |
|
print_close_stag : |
}
特定于格式化程序的语义标签处理函数: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
缓冲区 b
,Format.formatter_of_buffer
b
返回一个新的格式化程序,使用缓冲区 b
作为其输出设备。类似地,给定一个 out_channel
输出通道 oc
,Format.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
初始域的标准格式化程序,用于写入标准输出。
val get_std_formatter : unit -> formatter
get_std_formatter ()
返回当前域的标准格式化程序,用于写入标准输出。
val err_formatter : formatter
初始域的格式化程序,用于写入标准错误。
val get_err_formatter : unit -> formatter
get_err_formatter ()
返回当前域的格式化程序,用于写入标准错误。
val formatter_of_buffer : Buffer.t -> formatter
formatter_of_buffer b
返回一个新的格式化程序,写入缓冲区 b
。在漂亮打印结束时,必须使用 Format.pp_print_flush
或 Format.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
:
let sob = make_symbolic_output_buffer ()
。然后使用以下方法定义符号格式化程序:let ppf = formatter_of_symbolic_output_buffer sob
像往常一样使用符号格式化程序 ppf
,并在漂亮打印结束时通过使用以下方法刷新符号输出缓冲区 sob
来检索符号项目:
flush_symbolic_output_buffer sob
.type
symbolic_output_item =
| |
Output_flush |
(* | 符号刷新命令 | *) |
| |
Output_newline |
(* | 符号换行命令 | *) |
| |
Output_string of |
(* |
| *) |
| |
Output_spaces of |
(* |
| *) |
| |
Output_indent of |
(* |
| *) |
符号漂亮打印机生成的项目
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
的内容并重置缓冲区 sob
。flush_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
在 ppf
上格式化 iter
对值集合 v
的迭代,使用 pp_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_sep
(pp_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_sep
(pp_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_sep
(pp_sep
默认为 Format.pp_print_cut
。在空序列上不执行任何操作。
此函数不会在无限序列上终止。
val pp_print_text : formatter -> string -> unit
pp_print_text ppf s
使用 Format.pp_print_space
和 Format.pp_force_newline
分别打印 s
中的空格和换行符。
val pp_print_option : ?none:(formatter -> unit -> unit) ->
(formatter -> 'a -> unit) -> formatter -> 'a option -> unit
pp_print_option ?none pp_v ppf o
使用 pp_v
在 ppf
上打印 o
,如果 o
为 Some v
,则打印 none
,如果它为 None
。默认情况下,none
不打印任何内容。
val pp_print_result : ok:(formatter -> 'a -> unit) ->
error:(formatter -> 'e -> unit) ->
formatter -> ('a, 'e) result -> unit
pp_print_result ~ok ~error ppf r
使用 ok
在 ppf
上打印 r
,如果 r
为 Ok _
,则使用 error
打印,如果 r
为 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
使用 left
在 ppf
上打印 e
,如果 e
为 Either.Left _
,则使用 right
打印,如果 e
为 Either.Right _
。
模块 Format
提供了一套完整的类似于 printf
的函数,用于使用格式字符串规范进行漂亮打印。
可以在格式字符串中添加特定的注释,以便为漂亮打印引擎提供漂亮打印命令。
这些注释使用 @
字符引入格式字符串中。例如,@
表示空格断开,@,
表示切割,@[
打开一个新框,@]
关闭最后一个打开的框。
val fprintf : formatter -> ('a, formatter, unit) format -> 'a
fprintf ff fmt arg1 ... argN
根据格式字符串 fmt
格式化参数 arg1
到 argN
,并在格式化程序 ff
上输出结果字符串。
格式字符串 fmt
是一个包含三种类型对象的字符字符串:普通字符和在 Printf
模块中指定的转换规范,以及特定于 Format
模块的漂亮打印指示。
漂亮打印指示字符由 @
字符引入,其含义如下:
@[
:打开一个漂亮打印框。框的类型和偏移量可以使用以下语法可选地指定:<
字符,后跟可选的框类型指示,然后是可选的整数偏移量,以及关闭的 >
字符。漂亮打印框类型是 h
、v
、hv
、b
或 hov
之一。' h
'代表'水平'漂亮打印框,' v
'代表'垂直'漂亮打印框,' hv
'代表'水平/垂直'漂亮打印框,' b
'代表一个'水平或垂直'漂亮打印框,显示缩进,' hov
'代表一个简单的'水平或垂直'漂亮打印框。例如,@[<hov 2>
使用 open_hovbox 2
打开一个缩进为 2 的'水平或垂直'漂亮打印框。有关漂亮打印框的更多详细信息,请参阅各种框打开函数 open_*box
。@]
:关闭最近打开的漂亮打印框。@,
:输出“切割”断点提示,如 print_cut ()
。@
:输出“空格”断点提示,如 print_space ()
。@;
:输出“完整”断点提示,如 print_break
。断点提示的 nspaces
和 offset
参数可以使用以下语法可选地指定:<
字符,后跟整数 nspaces
值,然后是整数 offset
,以及关闭的 >
字符。如果未提供参数,则完整断点默认为“空格”断点提示。@.
:刷新漂亮打印机并拆分行,如 print_newline ()
。
@<n>
:将后续项打印成长度为n
的样子。因此,printf "@<0>%s" arg
将arg
打印成一个零长度字符串。如果@<n>
后面没有跟转换说明符,则格式中的下一个字符将被打印成长度为n
的样子。@{
:打开一个语义标签。标签的名称可以可选地用以下语法指定:<
字符,后跟一个可选的字符串说明,以及结束的>
字符。字符串说明是任何不包含结束字符'>'
的字符字符串。如果省略,则标签名称默认为空字符串。有关语义标签的更多详细信息,请参阅函数Format.open_stag
和Format.close_stag
。@}
:关闭最近打开的语义标签。@?
:像print_flush ()
一样刷新漂亮打印程序。这等效于转换%!
。@\n
:强制换行,如force_newline ()
一样,而不是漂亮打印的正常方式,你应该更倾向于在垂直漂亮打印框内使用断行提示。注意:为了防止将@
字符解释为漂亮打印指示,请使用%
字符将其转义。旧的引用模式@@
已弃用,因为它与字符'@'
的格式化输入解释不兼容。
示例: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_formatter
的fprintf
,并调用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。
我们有一个l
对列表(int * bool)
,顶层会为我们打印它
# 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)]
简而言之,它所做的事情是
pp_pair
打印一个用"(" ")"括起来的bool*int
对。它接受一个格式化程序(格式化发生在其中),以及该对本身。打印完成后,它返回()
。Format.printf "l = [@[<hov>%a@]]@." ... l
类似于printf
,但具有其他格式化指令(用"@"表示)。对"@[<hov>
"和"@]
"是一个“水平或垂直框”。Format.formatter
的状态。不要在Format
中使用"\n"。printf
的"%d"或"%s"一样。但是,"%d"打印整数,"%s"打印字符串,而"%a"则接受一个打印机(类型为Format.formatter -> 'a -> unit
)和一个值(类型为'a
),并将打印机应用于该值。这是打印机组合性的关键。Format.pp_print_list ~pp_sep:(...) pp_pair
构建一个列表打印机。pp_print_list
接受一个元素打印机并返回一个列表打印机。如果提供了?pp_sep
可选参数,则在每个元素之间调用它以打印分隔符。(fun out () -> Format.fprintf out ";@ ")
。它打印";",然后打印"@ ",这是一个断行空格(如果框即将溢出,它要么打印" ",要么打印换行符)。这个"@ "负责将列表打印拆分为多行。如果我们省略"@ ",我们会得到一个难看的单行打印
# 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”是一个小数点后最多3位的float
打印机;"%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派生器来自动从类型定义派生漂亮打印机。