格式化输出io:format的奇技淫巧
格式化输出io:format
是我接触Erlang使用的第一个库函数(io:format("Hello World")
),随着学习的深入,它也是我debug优先选择最简单直接的工具。
不过它除了简单的输出外,还有很多进阶用法。甚至通过它,你就可以在命令行画出精艳的图表。比如:我在Visualize Erlang/Elixir Nodes On The Command Line observer_cli
中绘制的与htop
类似图表。
同时这个API的选项特别多,却又非常好用,你完全可以不必了解这些选项(默认值)完成一些简单的需求,也可以使用选项来定制复杂的需求,设计者在扩展性强和易用性间的平衡做得非常到位,这也给我们自己设计API提供了一种参考。
API接口说明
format(Format) -> format(Format,[]).
format(Format, Data) -> format(group_leader(),Format,Data).
format(IoDevice, Format, Data) -> ok
IoDevice = device()
Format = format()
Data = [term()]
device()
I/O驱动,可以是标准的standard_io
, standard_error
,也可以是一个使用file:open/2
打开处理I/O协议的pid
(或register name
),比如:
%% 在当前目录下test.txt文件(没有则创建)中写入二进制的<<"good">>
{ok, IoDevice} = file:open("test.txt", [write,binary]),
io:format(IoDevice, <<"good">>, []),
ok = file:close(IoDevice).
format()
可以是atom
, string
, binary
,但最终都会使用(atom_to_list/1,binary_to_list/1
)转化为list
,所以最佳实践是直接使用list
。Format是要和Data搭配使用的,比如最常见的:
> io:format("this is a ~s from ~w~n", ["hello world", erlang]).
this is a hello world from erlang
就是把”hello world”
,erlang
依次填充到对应的占位符。接下来系统的了解一下这些占位符。
Format通常格式为:
~F.P.PadModC
其中F,P,PadMod
都是可以缺省(采用默认值),所以灵活性非常高!
F
Field Width 输出内容的总宽度,如果是负数,则左对齐;正数则右对齐;缺省(不指定)就是使用输出内容的实际长度。如果指定的长度小于实际长度,整个输出内容就使用*
代替。比如:
io:format("|~w|~n", [1234567890]). %% 缺省输出实际长度
|1234567890|
io:format("|~-20w|~n", [1234567890]). %% 负数为左对齐
|1234567890 |
io:format("|~20w|~n", [1234567890]). %% 正常为右对齐
| 1234567890|
io:format("|~9w|~n", [1234567890]). %% 指定宽度小于实际宽度为*
|*********|
P
Precision 精度,默认值为不指定,精度和输出内容的控制符(C
)密切有关。比如:
io:format("|~.1f|~n", [1234567890.123]). %%浮点数: 小数个数>=精度时四舍五入小数位个数
|1234567890.1|
io:format("|~.10f|~n", [1234567890.123]). %%浮点数: 小数个数<精度时补全小数个数
|1234567890.1229999065|
io:format("|~.1s|~n", ["abcd”]). %%字符串长度>=精度截断字符串的长度。
|a|
io:format("|~.10s|~n", ["abcd"]). %%字符串长度<精度补全字符串的长度(默认使用空填充)。
|abcd |
Pad
Padding 填充字符,这个是用来填充F
和P
不够位数时的填充内容,默认为空‘ ’ ,有且仅能指定一个字符。
io:format("|~25.10.xs|~n", ["abcd"]). %% 把P和F都填充为了x
|xxxxxxxxxxxxxxxabcdxxxxxx|
io:format("|~25.10.Af|~n", [1234567890.123]). %% 把P填充为了字符A
|AAAA1234567890.1229999065|
Mod
Modifier 修饰词,只能为一个字符(t为翻译Unicode友好输出,l是禁止p和P转义可打印字符输出内容,使用最原始的格式输出。
io:format("~ts~n”, ["中国"]). %% unicode转义
中国
io:format("~p~n”, ["中国"]). %% 原始输出
[20013,22269]
io:format("~p~n”, [[65]]). %% 转义ASCILL
"A"
io:format("~lp~n”, [[65]]). %% 原始输出
[65]
C
Control Sequences 控制序列,根据Data的类型,可用的C
有以下几种。
特殊字符
~ 波浪号: 因为是format里面的转义,所以当需要真正输出需要转义。
n 换行: 这个不需要解释,新启一行。
i 忽略: ignore,忽略下一个参数。io:format("|~i|~n", [good]).
输出为||
。ASCILL code
~c
,输出字符只能小于225,当为unicode时使用~tc
。精度为把字符重复输出多少位。io:format("|~10.5c|~-10.5c|~5c|~n", [$a, $b, $c]). %% 不指定精度时,默认与F同精度。
| aaaaa|bbbbb |ccccc|
io:format(“~tc~n",[1024]).
\x{400}
io:format(“~c~n",[1024]).
^@
浮点数
无Mod参数(无指定修饰符),为~F.Pf
F为总宽度,P为小数点位,默认P为6,且>=2io:format("~f~n", [10.1234567]). %%默认小数点6位且四舍五入
10.123457
如果你只想规定小数位个数,而不限制总长度时(不限整数位),可以使用
io:format("~.2f~n", [10.1234567]).
10.12
科学记数法
无Mod(无指定修饰符),为~F.Pe
F
为总宽度,P
为小数点位,默认P
为6,且>=2
特点注意的是P
是小数点的位数+1(有一位为e
占领了)io:format(“~e~n”, [10.1234567]).
1.01235e+1
浮点数与科学记数结合体
~g
如果0.1=<Data<10000.0
时使用f
输出,否则使用e
输出。io:format("|~22.4g|~n", [102222.1234567]).
| 1.022e+5|
字符串输出
~s
默认为精度就是实际宽度,~ts
为unicode转义输出,与其它控制符不同的是,如果宽度超过指定的精度或宽度,不会输出*
,只会截断字符串。io:format("|~8.5.as|~n", ["1234567890"]). %% 截断为5位,总长为8位,使用a填充不足的3位
|aaa12345|
如果使用
~s
去转义>255的字符,会报错。需要指定为~ts
,所以如果不能明确范围,统一使用~ts
io:format("~s~n",[[1024]]).
** exception error: bad argument
in function io:format/3
called as io:format(<0.53.0>,"~s~n",[[1024]])
io:format("~ts~n",[[1024]]).
Ѐ
Erlang任意term()
~w
使用标准语法输出Erlang的term(),Atom如果有不可打印字符会加上单引号‘’,如果Atom字符大于255,会直接输出,友好输出请加上~tw
,浮点数会输出实际最短(可能会四舍五入),```
io:format("~w~n”, ['Ѐ']).
'\x{400}'
io:format("~tw~n”, ['Ѐ']).
'Ѐ'
```
~p
同~w
一样,但是更强大,会使用多行输出字符串,不支持左对齐,会尝试把可打印的字符都转成string
单行最大宽度默认为80,精度确定了最初始(第一行)的宽度。1> T = [{attributes,[[{id,age,1.50000},{mode,explicit},
{typename,"INTEGER"}], [{id,cho},{mode,explicit},{typename,'Cho'}]]},
{typename,'Person'},{tag,{'PRIVATE',3}},{mode,implicit}].
...
2> io:format(“~w~n", [T]).
[{attributes,[[{id,age,1.5},{mode,explicit},{typename,
[73,78,84,69,71,69,82]}],[{id,cho},{mode,explicit},{typena
me,'Cho'}]]},{typename,'Person'},{tag,{'PRIVATE',3}},{mode
,implicit}]
ok
3> io:format(“~62p~n", [T]).
[{attributes,[[{id,age,1.5},
{mode,explicit},
{typename,"INTEGER"}],
[{id,cho},{mode,explicit},{typename,'Cho'}]]},
{typename,'Person'},
{tag,{'PRIVATE',3}},
{mode,implicit}]
4> io:format(“Here T = ~64p~n", [T]).
Here T = [{attributes,[[{id,age,1.5},
{mode,explicit},
{typename,"INTEGER"}],
[{id,cho},
{mode,explicit},
{typename,'Cho'}]]},
{typename,'Person'},
{tag,{'PRIVATE',3}},
{mode,implicit}]
ok
5> io:format(“Here T = ~64.10p~n", [T]).
Here T = [{attributes,[[{id,age,1.5},
{mode,explicit},
{typename,"INTEGER"}],
[{id,cho},
{mode,explicit},
{typename,'Cho'}]]},
{typename,'Person'},
{tag,{'PRIVATE',3}},
{mode,implicit}]
如果使用
~lp
,就不会尝试把printable character转化。6> S = [{a,"a"}, {b, "b"}].
7> io:format(“~15p~n", [S]).
[{a,"a"},
{b,"b"}]
ok
8> io:format(“~15lp~n", [S]).
[{a,[97]},
{b,[98]}]
ok
大写
W
和小写的w
一样,不过可以加一个额外的参数指定最大的深度,超过了就只打印出…
9> io:format("~W~n", [T,4]).
[{attributes,[...]},{typename,...},{...}|...]
大写
P
和小写的p
一样,不过可以加一个额外的参数指定最大的深度,超过了就只打印出…
10> io:format("~P~n", [T,9]).
[{attributes,[[{id,age,1.5},{mode,explicit},{typename,...}],
[{id,cho},{mode,...},{...}]]},
{typename,'Person'},
{tag,{'PRIVATE',3}},
{mode,implicit}]
- 按2-36进制输出整数
~B
默认为10进制,精度P指定几进制。小写的b就是使用小写字母输出>io:format(“~.16B~n", [31]).
1F
>io:format(“~.2B~n", [-19]).
-10011
>io:format(“~.36B~n”, [6*36+35]).
6Z
~X
大写的X,和B一样,不过可以加额外的参数。
小写的x就是使用小写字母输出> io:format(“~X~n", [31,"10#"]).
10#31
> io:format(“~.16X~n", [-31,"0x"]).
-0x1F
~#
和B
一样,但是可以输出进制的base
~+
就是使用小写字母输出> io:format(“~.10#~n", [31]).
10#31
> io:format(“~.16#~n", [-31]).
-16#1F
>io:format(“~.16+~n", [-31]).
-16#1f
扩展阅读
- Group Leader
一般调试时都是直接调用io:format(Format, Data)
,缺省了IoDevice为group_leader()
,这在本地调试时是可以正常工作的,如果我们使用rpc
来操作远程节点,就分2种情况。比如:
rpc调用的函数中明明有运行了io:format,却不能在远程节点上输出内容。因为rpc:call新创建的进程的group_leader()为使用rpc:call的节点,所以打印内容会显示在本节点上。
如果想在远程节点打印,可以指定IoDevice为一特殊的进程user
。
rpc:call(`remote@ip`, io, format, ["test~p~n", erlang:time()]). %%在本节点打印
rpc:call(`remote@ip`, io, format, [user, "test~p~n", erlang:time()]). %%在远程节点打印
- ANSI colors
在Erlang Shell中我们可以用ANSI colors来让我们的内容更加好看。Elixir
的shell就是这样的,对此还有一个专门的模块来处理ANSI(IO.ANSI
), 但是在Erlang就需要我们自己来定义,也可以使用这个第三方库(非常简单)erlang-color - 如何清屏或把输出内容的起点移到最上面的特殊字符。
io:format("\e[H\e[J"). %% 清屏,实现linux clear命令的效果
io:format(\e[H"). %% 把输出内容的起点移动最上面开始写,不会清除旧的输出,但是会覆盖。
输出友好的时间格式最佳实现
lager最开始是使用io_lib
:localtime_ms() ->
{_, _, Micro} = Now = os:timestamp(),
{Date, {Hours, Minutes, Seconds}} = calendar:now_to_local_time(Now),
{Date, {Hours, Minutes, Seconds, Micro div 1000 rem 1000}}. format_time() ->
format_time(localtime_ms()). format_time({{Y, M, D}, {H, Mi, S, Ms}}) ->
{io_lib:format("~b-~2..0b-~2..0b", [Y, M, D]),
io_lib:format("~2..0b:~2..0b:~2..0b.~3..0b", [H, Mi, S, Ms])};
format_time({{Y, M, D}, {H, Mi, S}}) ->
{io_lib:format("~b-~2..0b-~2..0b", [Y, M, D]),
io_lib:format("~2..0b:~2..0b:~2..0b", [H, Mi, S])}.
因为这个函数调用的频次非常高,但
io_lib
的速度不是很满意,所以就优化为了以下:format_time({utc, {{Y, M, D}, {H, Mi, S, Ms}}}) ->
{[integer_to_list(Y), $-, i2l(M), $-, i2l(D)],
[i2l(H), $:, i2l(Mi), $:, i2l(S), $., i3l(Ms), $ , $U, $T, $C]};
format_time({{Y, M, D}, {H, Mi, S, Ms}}) ->
{[integer_to_list(Y), $-, i2l(M), $-, i2l(D)],
[i2l(H), $:, i2l(Mi), $:, i2l(S), $., i3l(Ms)]};
format_time({utc, {{Y, M, D}, {H, Mi, S}}}) ->
{[integer_to_list(Y), $-, i2l(M), $-, i2l(D)],
[i2l(H), $:, i2l(Mi), $:, i2l(S), $ , $U, $T, $C]};
format_time({{Y, M, D}, {H, Mi, S}}) ->
{[integer_to_list(Y), $-, i2l(M), $-, i2l(D)],
[i2l(H), $:, i2l(Mi), $:, i2l(S)]}.
i2l(I) when I < 10 -> [$0, $0+I];
i2l(I) -> integer_to_list(I).
i3l(I) when I < 100 -> [$0 | i2l(I)];
i3l(I) -> integer_to_list(I).
几个很有用的辅助
Marco
类型 作用 ?MODULE 当前模块 ?LINE 当前文件行数 ?FUNCTION 当前运行函数 ??VAR 当前输出的变量名字 比如在module中定义:
-ifndef(PRINT).
-define(PRINT(Var), io:format("DEBUG: ~p:~p - ~p=~p~n~n", [?MODULE, ?LINE, ??Var, Var])).
-endif.
MyValue = test_value,
?PRINT(MyValue). %%调用
DEBUG: ModuleName:FileLine - MyValue=test_value
所有形式的知識最終意味著自我的認知。--李小龙
格式化输出io:format的奇技淫巧的更多相关文章
- Python 3.x 格式化输出字符串 % & format 笔记
Python 3.x 格式化输出字符串 % & format 笔记 python格式化字符串有%和{}两种 字符串格式控制符. 字符串输入数据格式类型(%格式操作符号) %%百分号标记 %c字 ...
- 格式化输出=========》format 和 %
str.format() 实现格式化输出的功能 s1 = "i am {0},gae{1}".format("alex",18) 普通版,直接输入元祖 ...
- 格式化输出%与format
一.%的用法 1.1整数输出 %o —— oct 八进制 : %d —— dec 十进制 : %x —— hex 十六进制 >>> print('%o' % 20) 24 >& ...
- python格式化输出之format用法
format用法 相对基本格式化输出采用‘%’的方法,format()功能更强大,该函数把字符串当成一个模板,通过传入的参数进行格式化,并且使用大括号‘{}’作为特殊字符代替‘%’ 使用方法由两种:b ...
- python格式化输出(% format用法)
%基本用法: 十进制输出:print('%d' % 6) 6也可以换成其它的数字变量 八进制输出:print('%o' % 6) 6也可以换成其它的数字变量 字符串输出:print('%s' ...
- python:格式化输出 str.format()
官网说明:https://docs.python.org/2/library/string.html#formatstrings python的格式输出有两种方法: 1.“ %s”.(variant) ...
- 超详细的格式化输出(format的基本玩法)
一.format的基本玩法 一.什么是format format是字符串内嵌(字符串内嵌:字符串中再嵌套字符串,加入双引号或单引号)的一个方法,用于格式化字符串.以大括号{}来标明被替换的字符串 fo ...
- python的格式化输出(format,%)
皇城PK Python中格式化字符串目前有两种阵营:%和format,我们应该选择哪种呢? 自从Python2.6引入了format这个格式化字符串的方法之后,我认为%还是format这根本就不算个问 ...
- python字符串格式化输出 %和format举例
#!/usr/bin/env python # -*- coding: UTF-8 -*- #pyversion:python3.5 #owner:fuzj s1 = "i am %s, i ...
随机推荐
- angularjs 缓存详解
一.什么是缓存 一个缓存就是一个组件,它可以透明地存储数据,以便未来可以更快地服务于请求. 缓存能够服务的请求越多,整体系统性能就提升得越多. 二.Angular 中的缓存 2.1 $cacheFac ...
- Pandas与Matplotlib基础
pandas是Python中开源的,高性能的用于数据分析的库.其中包含了很多可用的数据结构及功能,各种结构支持相互转换,并且支持读取.保存数据.结合matplotlib库,可以将数据已图表的形式可视化 ...
- 【原创】快应用QuickApp--HelloWorld体验
快应用: 快应用是九大手机厂商基于硬件平台共同推出的新型应用生态.用户无需下载安装,即点即用,享受原生应用的性能体验. 3月20日在北京联合召开快应用标准启动发布会.过去1天了,官网(快应用官方网站) ...
- CentOS7.2下Nginx的使用
Nginx的启动 指定配置文件的方式启动 nginx -c /etc/nginx/nginx.conf 对于yum安装的nginx,使用systemctl命令启动 systemctl start ng ...
- CSS(CSS3)选择器(2)
该部分主要为CSS3新增的选择器 接上一篇 CSS(CSS3)选择器(1) 一.通用兄弟选择器: 24:E ~ F,匹配任何E元素之后的同级F元素. div ~ p{ background-color ...
- 【数据库】数据库的锁机制,MySQL中的行级锁,表级锁,页级锁
转载:http://www.hollischuang.com/archives/914 数据库的读现象浅析中介绍过,在并发访问情况下,可能会出现脏读.不可重复读和幻读等读现象,为了应对这些问题,主流数 ...
- 【Flask】 WTForm表单编程
WTForm表单编程 在网页中,为了和用户进行信息交互总是不得不出现一些表单.flask设计了WTForm表单库来使flask可以更加简便地管理操作表单数据.WTForm中最重要的几个概念如下: Fo ...
- 【Python】 多线程并发threading & 任务队列Queue
threading python程序默认是单线程的,也就是说在前一句语句执行完之前后面的语句不能继续执行(不知道我理解得对不对) 先感受一下线程,一般情况下: def testa(): sleep(1 ...
- 【Python】 zabbixAPI的包装pyzabbix
pyzabbix pyzabbix是zabbixAPI的第三方python包装.从网上莫名其妙地搞到了一份源码,看了一下之后发现实现方法还蛮巧妙的,感觉挺好的就记下来了.那些个源码本身其实也是一个个单 ...
- 解决数据库mysql插入乱码问题
当我们遇到mysql乱码问题的时候,一般要修改my.ini文件: 我遇到的是两个版本,一个是mysql5.5版本,另一个是mysql5.7 5.5的是在这个目录下面:C:\Program Files\ ...