io_lib(3)

πŸ“¦
version 29.0.6 Β· 3 versions
, in ubuntu stonking

πŸ’– NAME πŸ’–

io_lib β€” the I/O library functions, bestie. Yes, that one. The vibe is immaculate, the character lists are deep, and honestly? We're obsessed. 🫢


🌸 DESCRIPTION 🌸

Okay so, this module is literally the home of all the functions that convert to and from strings (and by strings we mean lists of characters, iykyk). They're the ones doing the heavy lifting behind the scenes for the functions in the io module. Main character energy, no cap. πŸ‘‘

Big heads-up tho: nobody promised you that the character lists coming back out of some of these functions are flat. Nope. They can absolutely be deep lists. If that's giving you chaos, just hit up lists:flatten/1 and it'll flatten those deep lists right out for you. πŸ’…


🧁 DATA TYPES 🧁

chars() :: [char() | chars()].

A possibly deep list that contains only char/0s. That's it, that's the whole tea. β˜•

chars_limit() :: integer().

A soft limit on how many characters get returned, okay bestie? The moment that character count is reached, all the remaining structures get replaced by "...". And CharsLimit defaults to -1, which literally means "no limit on the number of characters returned". Live free, babe. πŸ¦‹

-opaque continuation()

A continuation exactly as returned by fread/3. Keep it safe, it's shy. πŸ™ˆ

depth() :: -1 | non_neg_integer().

format_options() :: [{chars_limit, chars_limit()}].

Options that can be passed to format/3 and fwrite/3. Slay.

format_spec()

format_spec() ::
          #{control_char := char(),
            args := [any()],
            width := none | integer(),
            adjust := left | right,
            precision := none | integer(),
            pad_char := char(),
            encoding := unicode | latin1,
            strings := boolean(),
            maps_order => maps:iterator_order()}.

A map describing the contents of a format string. Let's break it down, babes:

  • control_char is the type of control sequence: $P, $w, and so on. ✨
  • args is a list of the arguments used by the control sequence, or an empty list if the control sequence does not take any arguments.
  • width is the field width.
  • adjust is the adjustment.
  • precision is the precision of the printed argument.
  • pad_char is the padding character.
  • encoding is set to true if translation modifier t is present.
  • strings is set to false if modifier l is present.
  • maps_order is set to undefined by default, ordered if modifier k is present, or reversed or CmpFun if modifier K is present.

fread_error() ::

atom | based | character | float | format | input | integer | string | unsigned.

fread_item() :: string() | atom() | integer() | float().

latin1_string() :: [unicode:latin1_char()].


✨ FUNCTIONS ✨


bfwrite(Format, Data, Options) -> unicode:unicode_binary()

when
    Format :: io:format(),
    Data :: [term()],
    Options :: format_options().

It's the binary variant of fwrite/3, periodt. πŸ’…

Returns a UTF-8 encoded binary string.


build_text(FormatList) -> chars()

when FormatList :: [char() | format_spec()].

For the details, go peek at scan_format/2. πŸ‘€


bwrite(Term, Options) -> unicode:unicode_binary()

when
    Term :: term(),
    Options :: [Option],
    Option ::
        {chars_limit, CharsLimit} |
        {depth, Depth} |
        {encoding, latin1 | utf8 | unicode} |
        {maps_order, maps:iterator_order()},
    CharsLimit :: chars_limit(),
    Depth :: depth().

Acts like write/2 does, except it hands you back a UTF-8 encoded binary string instead. Same energy, different wrapper. 🎁


bwrite_string(String, Qoute, InEnc) -> unicode:unicode_binary()

when
    String :: string() | binary(),
    Qoute :: integer() | [],
    InEnc :: unicode | latin1.

Returns the UTF-8 encoded binary String surrounded by Qoute. (And yes, it's literally spelled Qoute in the signature β€” we don't make the rules, we just respect them. 😌)


char_list(Term) -> boolean()

when Term :: term().

Returns true if Term is a flat list of characters in the Unicode range β€” otherwise false. Flat. Not deep. We said what we said. πŸ’€


deep_char_list(Term) -> boolean()

when Term :: term().

Returns true if Term is a possibly deep list of characters in the Unicode range, otherwise false. Now this one accepts depth. Growth. 🌱


deep_latin1_char_list(Term) -> boolean()

when Term :: term().

Returns true if Term is a possibly deep list of characters in the ISO Latin-1 range, otherwise false.


fread(Format, String) -> Result

when
    Format :: string(),
    String :: string(),
    Result ::
        {ok,
         InputList :: [fread_item()],
         LeftOverChars :: string()} |
        {more,
         RestFormat :: string(),
         Nchars :: non_neg_integer(),
         InputStack :: chars()} |
        {error, {fread, What :: fread_error()}}.

Tries to read String according to whatever control sequences live in Format. πŸ”

For the full detailed description of all the formatting options available, check io:fread/3. And heads up: this function assumes that String contains whole lines. Don't be out here feeding it half a line and then acting surprised. 😭

Here's what it returns:

  • {ok, InputList, LeftOverChars} β€” the string was read, bestie! InputList is the list of successfully matched and read items, and LeftOverChars are the input characters that weren't used.
  • {more, RestFormat, Nchars, InputStack} β€” the string was read, but more input is needed to complete the original format string. RestFormat is the remaining format string, Nchars is the number of characters scanned, and InputStack is the reversed list of inputs matched up to that point.
  • {error, What} β€” the read operation failed, and the parameter What gives a hint about what went wrong. Not the vibe. πŸ’€

Example, so you can see her in action:

3> io_lib:fread("~f~f~f", "15.6 17.3e-6 24.5").
{ok,[15.6,1.73e-5,24.5],[]}

fread(Continuation, CharSpec, Format) -> Return

when
    Continuation :: continuation() | [],
    CharSpec :: string() | eof,
    Format :: string(),
    Return ::
        {more, Continuation1 :: continuation()} |
        {done, Result, LeftOverChars :: string()},
    Result ::
        {ok, InputList :: [fread_item()]} |
        eof |
        {error, {fread, What :: fread_error()}}.

THIS is the re-entrant formatted reader, and she's iconic. πŸ”₯

⚠️ Important: the continuation of the first call to this function must be []. Do not freestyle this. Please.

For a complete description of how the re-entrant input scheme actually works, see Armstrong, Virding, Williams: 'Concurrent Programming in Erlang', Chapter 13.

Here's what it returns:

  • {done, Result, LeftOverChars} β€” the input is complete. And the result is one of these:
    • {ok, InputList} β€” the string was read. InputList is the list of successfully matched and read items, and LeftOverChars are the remaining characters.
    • eof β€” end of file was encountered. LeftOverChars are the input characters not used.
    • {error, What} β€” an error occurred and the parameter What gives a hint about the error.
  • {more, Continuation} β€” more data is required to build a term. This Continuation must be passed to fread/3 when more data becomes available.

fwrite(Format, Data) -> chars()

when Format :: io:format(), Data :: [term()].

Returns a character list that represents Data formatted according to Format. πŸ’–

For a detailed description of all the available formatting options, see io:fwrite/1,2,3. And watch out:

🚨 If the format string or the argument list contains an error, a fault is generated. Not a soft little error. A fault. Be careful bestie.

If and only if the Unicode translation modifier is used in the format string (that is, ~ts or ~tc), the resulting list can contain characters beyond the ISO Latin-1 character range (that is, numbers > 255). And if that happens, the result is still an ordinary string/0, and it can absolutely be used in any context where Unicode data is allowed. Slay. ✨


fwrite(Format, Data, Options) -> chars()

when
    Format :: io:format(),
    Data :: [term()],
    Options :: format_options().

Returns a character list that represents Data formatted according to Format, in exactly the same way as fwrite/2 and format/2 β€” except she takes an extra argument, a list of options. Fancy. πŸ’…

The valid option:

  • {chars_limit, CharsLimit} β€” a soft limit on the number of characters returned. When the number of characters is reached, the remaining structures are replaced by " ...". CharsLimit defaults to -1, which means no limit on the number of characters returned.

indentation(String, StartIndent) -> integer()

when String :: string(), StartIndent :: integer().

Returns the indentation if String has been printed, starting at StartIndent. That's the whole thing, she's a one-job girlie and she does her job. πŸŽ€


latin1_char_list(Term) -> boolean()

when Term :: term().

Returns true if Term is a flat list of characters in the ISO Latin-1 range, otherwise false.


nl() -> string().

Returns a character list that represents a new line character. Tiny. Humble. Essential. 🌈


when
    Term :: term(),
    Column :: non_neg_integer(),
    LineLength :: non_neg_integer(),
    Depth :: depth().

Returns a list of characters that represents Term β€” but here's the glow-up: it breaks representations longer than one line into many lines, and indents each line sensibly. So polite of her. πŸŽ€

She also tries to detect and output lists of printable characters as strings. Multi-talented.

  • Column is the starting column; defaults to 1.
  • LineLength is the maximum line length; defaults to 80. And check this β€” the value 0 can be used to specify infinite length, meaning that no line breaks are inserted. She said no limits. πŸ¦‹
  • Depth is the maximum print depth; defaults to -1, which means no limitation.

printable_latin1_list(Term) -> boolean()

when Term :: term().

Returns true if Term is a flat list of printable ISO Latin-1 characters, otherwise false.


printable_list(Term) -> boolean()

when Term :: term().

Returns true if Term is a flat list of printable characters, otherwise false. Okay that's the last one for nowβ€”

okay so real quick, what even counts as a printable character here? That's literally decided by the +pc startup flag you pass to the Erlang VM. Wanna know more? Check out io:printable_range/0 and erl(1). iykyk πŸ’…


✨ printable_unicode_list ✨

printable_unicode_list(Term) -> boolean() when Term :: term().

Returns true if Term is a flat list of printable Unicode characters, otherwise it's giving false. No cap. πŸ¦‹


✨ scan_format ✨

scan_format(Format, Data) -> FormatList
                     when
                         Format :: io:format(),
                         Data :: [term()],
                         FormatList :: [char() | format_spec()].

Returns a list that matches the format string you gave, but it swapped out all the control sequences for their matching tuples. Then you can pass that list to:

  • build_text/1 to get the same effect as format(Format, Args)
  • unscan_format/1 to get the corresponding pair of Format and Args (with every * and its matching argument expanded to numeric values)

A super common glow-up for this function is swapping out unbounded-size control sequences like ~w and ~p for the depth-limited versions ~W and ~P before you format to text β€” like in, for example, a logger. Obsessed. πŸŽ€


✨ unscan_format ✨

unscan_format(FormatList) -> {Format, Data}
                       when
                           FormatList :: [char() | format_spec()],
                           Format :: io:format(),
                           Data :: [term()].

For all the tea, go see scan_format/2. πŸ’…


✨ write ✨

write(Term, Depth) -> chars()
               when Term :: term(), Depth :: depth();
           (Term, Options) -> chars()
               when
                   Term :: term(),
                   Options :: [Option],
                   Option ::
                       {chars_limit, CharsLimit} |
                       {depth, Depth} |
                       {encoding, latin1 | utf8 | unicode} |
                       {maps_order, maps:iterator_order()},
                   CharsLimit :: chars_limit(),
                   Depth :: depth().

Returns a character list that represents Term. The Depth option controls how deep into the structures it goes.

When you hit that depth, everything below it gets replaced with "..." β€” literally just dots.

Depth defaults to -1, which means no limit at all. The CharsLimit option puts a soft limit on the number of characters returned. When you hit that limit, the rest of the structures get replaced by "...". CharsLimit also defaults to -1, so no limit on characters returned either.

Example time, bestie:

1> lists:flatten(io_lib:write({1,[2],[3],[4,5],6,7,8,9})).
"{1,[2],[3],[4,5],6,7,8,9}"
2> lists:flatten(io_lib:write({1,[2],[3],[4,5],6,7,8,9}, 5)).
"{1,[2],[3],[...],...}"
3> lists:flatten(io_lib:write({[1,2,3],[4,5],6,7,8,9}, [{chars_limit,20}])).
"{[1,2|...],[4|...],...}"

✨ write_atom ✨

write_atom(Atom) -> chars() when Atom :: atom().

Returns the list of characters needed to print the atom Atom. Slay. ✨


✨ write_atom_as_latin1 ✨

write_atom_as_latin1(Atom) -> latin1_string() when Atom :: atom().

Returns the list of characters needed to print the atom Atom. Non-Latin-1 characters get escaped. πŸ’…


✨ write_char ✨

write_char(Char) -> chars() when Char :: char().

Returns the list of characters needed to print a character constant in the Unicode character set. Iconic. πŸ¦‹


✨ write_char_as_latin1 ✨

write_char_as_latin1(Char) -> latin1_string() when Char :: char().

Returns the list of characters needed to print a character constant in the Unicode character set. Non-Latin-1 characters get escaped. πŸ™ˆ


✨ write_latin1_char ✨

write_latin1_char(Latin1Char) -> latin1_string()
                           when Latin1Char :: unicode:latin1_char().

Returns the list of characters needed to print a character constant in the ISO Latin-1 character set. Periodt. πŸ’…


✨ write_latin1_string ✨

write_latin1_string(Latin1String) -> latin1_string()
                             when Latin1String :: latin1_string().

Returns the list of characters needed to print Latin1String as a string. So fetch. πŸ’–


✨ write_string ✨

write_string(String) -> chars() when String :: string().

Returns the list of characters needed to print String as a string. Main character energy. πŸ‘‘


✨ write_string_as_latin1 ✨

write_string_as_latin1(String) -> latin1_string()
                                when String :: string().

Returns the list of characters needed to print String as a string. Non-Latin-1 characters get escaped. No cap. πŸ’…


That's the tea! This page was brought to you by Ericsson AB, stdlib version 8.0.4, and it's the io_lib(3) manual. No date in the footer but we're timeless anyway. πŸ’…βœ¨

πŸ“¦ where this page comes from