Tricks of formatting output ioformat

Formatted output io:format is the first library function (io:format("Hello World") that I use when I come into contact with Erlang. With the in-depth study, it is also the simplest and direct tool I prefer for debug ging.
However, in addition to its simple output, it has many advanced uses. Even with it, you can draw brilliant charts on the command line.
At the same time, the API has many options, but it is very easy to use. You can complete some simple requirements without understanding these options (default values), and you can also use options to customize complex requirements. The designer has made a good balance between strong scalability and ease of use, which also provides a reference for our own API design.

API interface description

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 drivers can be standard_io, standard_error can also be a PID (or register name) that uses file:open/2 to open and process the I/O protocol, such as:

%% In the current directory test.txt file(Not created)Write binary in<<"good">>
{ok, IoDevice} = file:open("test.txt", [write,binary]),
io:format(IoDevice, <<"good">>, []),
ok = file:close(IoDevice).

format()

It can be atom, string or binary, but it will be used in the end_ to_ list/1,binary_ to_ List / 1) is converted to list, so the best practice is to use list directly. Format should be used with Data, such as the most common:

io:format("this is a ~s from ~w~n", ["hello world", erlang]).
this is a hello world from erlang

That is, fill "hello world" and erlang into the corresponding placeholders in turn. Next, take a systematic look at these placeholders.
The Format is usually:

~F.P.PadModC

Among them, F, P and padmod can be defaulted (the default value is adopted), so the flexibility is very high!

  • F Field Width is the total width of the output content. If it is a negative number, it is aligned to the left; Positive numbers are aligned to the right; The default (unspecified) is to use the actual length of the output. If the specified length is less than the actual length, the entire output is replaced by *. For example:
io:format("|~w|~n", [1234567890]). %% Default output actual length
|1234567890|
io:format("|~-20w|~n", [1234567890]). %% Negative numbers are left justified
|1234567890          |
io:format("|~20w|~n", [1234567890]).  %% Align right normally
|          1234567890|
io:format("|~9w|~n", [1234567890]).  %% The specified width is less than the actual width*
|*********|
  • P Precision precision. The default value is not specified. Precision is closely related to the control character (C) of the output content. For example:
io:format("|~.1f|~n", [1234567890.123]). %%Floating point number: Number of decimals>=The number of decimal places shall be rounded off during precision
|1234567890.1|
io:format("|~.10f|~n", [1234567890.123]). %%Floating point number: Number of decimals<Complete the number of decimals in precision
|1234567890.1229999065|
io:format("|~.1s|~n", ["abcd"]). %%String length>=Precision truncates the length of the string.
|a|
io:format("|~.10s|~n", ["abcd"]). %%String length<Precision completes the length of the string(Empty fill is used by default). 
|abcd      |
  • Pad Padding filling character, which is used to fill in the filling content when F and P are not enough. It is empty by default, and only one character can be specified.
io:format("|~25.10.xs|~n", ["abcd"]). %% hold P and F All filled for x
|xxxxxxxxxxxxxxxabcdxxxxxx|
io:format("|~25.10.Af|~n", [1234567890.123]). %% hold P Fill with characters A
|AAAA1234567890.1229999065|
  • Mod Modifier can only be one character (t is the translation Unicode friendly output, l is the output content that prevents P and P from escaping printable characters, and output in the most original format.
io:format("~ts~n", ["China"]). %% unicode Escape
 China
io:format("~p~n", ["China"]). %% Raw output
[20013,22269]
io:format("~p~n", [[65]]).  %% Escape ASCILL
"A"
io:format("~lp~n", [[65]]). %% Raw output
[65]
  • C Control Sequences. According to the type of Data, the following C sequences are available.

Special characters

~Tilde: because it is an escape in format, it needs to be escaped when real output is required.
n line feed: there is no need to explain this. Start a new line.
i ignore: ignore to ignore the next parameter. io:format("|~i|~n", [good]). The output is |.

ASCILL code

~c. The output character can only be less than 225. Use ~tc when it is unicode. The precision is how many bits the character is repeatedly output.

io:format("|~10.5c|~-10.5c|~5c|~n", [$a, $b, $c]). %% When the precision is not specified, the default is F Same precision.
|     aaaaa|bbbbb     |ccccc|
io:format("~tc~n",[1024]).
\x{400}
io:format("~c~n",[1024]).
^@

Floating point number

No Mod parameter (no modifier specified), it is ~ f.pf, f is the total width, P is the decimal place, the default P is 6, and > = 2

io:format("~f~n", [10.1234567]). %%The default is 6 decimal places and rounded
10.123457

If you only want to specify the number of decimal places without limiting the total length (not limited to integer digits), you can use

io:format("~.2f~n", [10.1234567]). 
10.12

Scientific notation

No mod (no specified modifier), it is ~ f.pe, f is the total width, P is the decimal place, the default P is 6, and > = 2
Note that P is the number of decimal places + 1 (one is occupied by e)

io:format("~e~n", [10.1234567]).
1.01235e+1

Combination of floating point number and scientific counting

~g if 0.1 = < data < 10000.0, use f output, otherwise use e output.

io:format("|~22.4g|~n", [102222.1234567]).
|              1.022e+5|

String output

~s defaults to precision, which is the actual width, ~ ts is unicode escape output. Different from other controllers, if the width exceeds the specified precision or width, no * will be output and only the string will be truncated.

io:format("|~8.5.as|~n", ["1234567890"]). %% Truncated to 5 bits, with a total length of 8 bits, using a Underfilled 3 bits
|aaa12345|

If you use ~ s to escape characters > 255, an error will be reported. It needs to be specified as ~ ts, so if the scope cannot be specified, ~ TS is used uniformly

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 arbitrary term()

  • ~w use the standard syntax to output Erlang's term(). Atom will add single quotation marks' 'if there are non printable characters. If the atom character is greater than 255, it will be output directly. Please add ~ tw for friendly output. The floating-point number will output the actual shortest (possibly rounded),

    io:format("~w~n", ['Ѐ']).
    '\x{400}'
    io:format("~tw~n", ['Ѐ']).
    'Ѐ'
    
  • ~p is the same as ~w, but more powerful. It can use multiple lines to output strings. It does not support left alignment. It will try to convert printable characters into strings
    The maximum width of a single line is 80 by default, and the precision determines the initial (first line) width.

    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}]
    

    If you use ~lp, you won't try to convert a 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
    
  • Uppercase W is the same as lowercase W, but you can add an additional parameter to specify the maximum depth. If it exceeds, only print out

    9> io:format("~W~n", [T,4]).
    [{attributes,[...]},{typename,...},{...}|...]
    
  • Uppercase P is the same as lowercase P, but you can add an additional parameter to specify the maximum depth. If it exceeds, only print out

    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}]
    

Output integer in binary 2-36

  • ~b defaults to decimal, and precision P specifies decimal. Lowercase b is output using lowercase letters

    >io:format("~.16B~n", [31]).
    1F
    >io:format("~.2B~n", [-19]).
    -10011
    >io:format("~.36B~n", [6*36+35]).
    6Z
    
  • ~X is capitalized with X, which is the same as B, but additional parameters can be added.
    A lowercase x is output using lowercase letters

    > io:format("~X~n", [31,"10#"]).
    10#31
    > io:format("~.16X~n", [-31,"0x"]).
    -0x1F
    
  • ~#It is the same as B, but it can output base in hexadecimal
    ~+Is to use lowercase letters for output

    > io:format("~.10#~n", [31]).
    10#31
    > io:format("~.16#~n", [-31]).
    -16#1F
    >io:format("~.16+~n", [-31]).
    -16#1f
    

Extended reading

  1. Group Leader
    Generally, io:format(Format, Data) is called directly during debugging, and the default IoDevice is group_leader(), which can work normally during local debugging. If we use rpc to operate remote nodes, there are two cases. For example:
    The function called by rpc clearly runs io:format, but it cannot output content on the remote node. Because rpc:call the group of the newly created process_ The leader () is a node that uses rpc:call, so the printed content will be displayed on this node.
    If you want to print on a remote node, you can specify IoDevice as a special process user.
rpc:call(`remote@ip`, io, format, ["test~p~n", erlang:time()]). %%Print in this node
rpc:call(`remote@ip`, io, format, [user, "test~p~n", erlang:time()]). %%Print at remote node
  1. ANSI colors
    In Erlang Shell, we can use ANSI colors To make our content better. Elixir's shell is like this. There is a special module to deal with ANSI(IO.ANSI), but we need to define it ourselves in Erlang. We can also use this third-party library (very simple) erlang-color
  2. How to clear the screen or move the starting point of output content to the top special character.
io:format("\e[H\e[J"). %% Clear screen, realize linux clear Effect of command
io:format(\e[H"). %% Move the starting point of the output content to the top and start writing. The old output will not be cleared, but will be overwritten.
  1. Best implementation of output friendly time format
    lager I started with 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])}.
    

    Because this function is called very frequently, but IO_ The speed of lib is not very satisfactory, So it's optimized for the following:

    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).
    
  2. Several useful aids, Marco

    typeeffect
    ?MODULECurrent module
    ?LINECurrent file lines
    ?FUNCTIONCurrently running function
    ??VARName of the currently output variable

    For example, define in 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). %%call
    DEBUG: ModuleName:FileLine - MyValue=test_value
    
    

Tags: Erlang io

Posted on Tue, 07 Sep 2021 20:06:18 -0400 by kulin