Источник IO
Функции для работы с вводом/выводом (IO).
Многие функции в этом модуле ожидают устройство ввода/вывода в качестве аргумента. Устройство ввода/вывода должно быть PID или атомом, представляющим процесс. Для удобства Elixir предоставляет :stdio и :stderr в качестве сокращений для :standard_io и :standard_error Erlang.
Большинство функций ожидают chardata. В случае передачи другого типа функции преобразуют эти типы в строку через протокол String.Chars (как показано в типовec). Более подробную информацию о chardata см. в разделе "Данные IO" ниже.
Устройства ввода/вывода
Устройство ввода/вывода может быть атомом или PID. В случае атома, атом должен быть именем зарегистрированного процесса. Кроме того, Elixir предоставляет два сокращения:
:stdio- сокращение для:standard_io, которое сопоставляется с текущимProcess.group_leader/0в Erlang:stderr- сокращение для именованного процесса:standard_errorв Erlang
Устройства ввода/вывода сохраняют свою позицию, что означает, что последующие вызовы функций чтения или записи будут начинаться с места, где устройство было в последний раз обработано. Позицию файлов можно изменить с помощью функции :file.position/2.
Данные IO
Данные IO — это тип данных, который может использоваться как более эффективный аналог бинарных данных в определенных ситуациях.
Значение типа данные IO — это двоичные данные или список, содержащий байты (целые числа в диапазоне 0..255 ) или вложенные данные IO. Тип рекурсивный. Вот пример одного из возможных представлений двоичных данных "hello" в виде данных IO:
[?h, "el", ["l", [?o]]]
Встроенный тип iodata/0 определен в терминах iolist/0. Список IO — это то же самое, что и данные IO, но он не допускает двоичных данных на верхнем уровне (но двоичные данные все же допускаются в самом списке).
Сценарии использования данных IO
Данные IO существуют потому, что часто требуется выполнить много операций добавления к меньшим фрагментам бинарных данных, чтобы создать большую двоичную строку. Однако в Erlang и Elixir конкатенация бинарных данных копирует конкатенированные бинарные данные в новую двоичную строку.
def email(username, domain) do username <> "@" <> domain end
В этой функции при создании адреса электронной почты будут скопированы бинарные данные username и domain. Теперь представьте, что вы хотите использовать полученный адрес электронной почты внутри другой бинарной строки:
def welcome_message(name, username, domain) do
"Welcome #{name}, your email is: #{email(username, domain)}"
end
IO.puts(welcome_message("Meg", "meg", "example.com"))
#=> "Welcome Meg, your email is: meg@example.com"
Каждый раз, когда вы конкатенируете двоичные данные или используете интерполяцию (#{}), вы делаете копии этих двоичных данных. Однако во многих случаях вам не нужна полная двоичная строка во время её создания, а только в конце, чтобы вывести её на печать или отправить куда-либо. В таких случаях вы можете создать двоичные данные, создав данные IO:
def email(username, domain) do
[username, ?@, domain]
end
def welcome_message(name, username, domain) do
["Welcome ", name, ", your email is: ", email(username, domain)]
end
IO.puts(welcome_message("Meg", "meg", "example.com"))
#=> "Welcome Meg, your email is: meg@example.com"
Создание данных IO дешевле, чем конкатенация бинарных данных. Конкатенация нескольких фрагментов данных IO просто означает их объединение в список, так как данные IO могут быть произвольно вложенными, и это дешёвая и эффективная операция. Большинство API на основе IO, такие как :gen_tcp и IO, получают данные IO и записывают их в сокет напрямую, не преобразуя их в двоичные данные.
Один недостаток данных IO заключается в том, что вы не можете выполнять такие действия, как сопоставление с образцом с первой частью данных IO, как с двоичными данными, потому что вы обычно не знаете форму данных IO. В таких случаях вам может потребоваться преобразовать их в двоичные данные, вызвав iodata_to_binary/1, что достаточно эффективно, так как реализовано нативно в C. Другие функции, например, вычисление длины данных IO, могут быть вычислены непосредственно на данных IO, вызвав iodata_length/1.
Chardata
Erlang и Elixir также имеют понятие chardata/0. Chardata очень похожи на данные IO: единственное отличие заключается в том, что целые числа в данных IO представляют байты, а целые числа в chardata — кодовые точки Юникода. Байты (byte/0) — это целые числа в диапазоне 0..255, а кодовые точки Юникода (char/0) — целые числа в диапазоне 0..0x10FFFF. Модуль IO предоставляет функцию chardata_to_string/1 для chardata как «аналог» функции iodata_to_binary/1 для данных IO.
Если вы попытаетесь использовать iodata_to_binary/1 с chardata, это приведёт к ошибке аргумента. Например, давайте попробуем поместить кодовую точку, которая не может быть представлена одним байтом, например, ?π, в данные IO:
IO.iodata_to_binary(["The symbol for pi is: ", ?π]) #=> ** (ArgumentError) argument error
Если мы используем chardata вместо этого, всё будет работать как ожидается:
iex> IO.chardata_to_string(["The symbol for pi is: ", ?π]) "The symbol for pi is: π"
Описание
Типы
Функции
- binread(device \\ :stdio, line_or_chars)
Считывает данные из устройства ввода/вывода
device. Операция небезопасна для Юникода.- binstream()
Возвращает потоковый ввод/вывод на основе строк, на основе
:stdio. Операция небезопасна для Юникода.- binstream(device \\ :stdio, line_or_bytes)
Преобразует устройство ввода/вывода
deviceв потоковый ввод/выводIO.Stream. Операция небезопасна для Юникода.- binwrite(device \\ :stdio, iodata)
Записывает
iodataв указанное устройствоdevice.- chardata_to_string(chardata)
Преобразует chardata в строку.
- getn(prompt, count \\ 1)
Получает количество байтов с устройства ввода/вывода
:stdio.- getn(device, prompt, count)
Получает количество байтов с устройства ввода/вывода
device.- gets(device \\ :stdio, prompt)
Считывает строку с устройства ввода/вывода
device.- inspect(item, opts \\ [])
Проверяет и записывает указанный
itemв стандартный вывод.- inspect(device, item, opts)
Проверяет
itemв соответствии с заданными параметрами, используя устройство ввода/выводаdevice.- iodata_length(iodata)
Возвращает размер данных IO.
- iodata_to_binary(iodata)
Преобразует данные IO в двоичные данные.
- puts(device \\ :stdio, item)
Записывает
itemв указанное устройствоdevice, аналогичноwrite/2, но добавляет перевод строки в конце.- read(device \\ :stdio, line_or_chars)
Считывает данные с устройства ввода/вывода
device.- stream()
Возвращает потоковый ввод/вывод на основе строк, на основе
:stdio.- stream(device \\ :stdio, line_or_codepoints)
Преобразует устройство ввода/вывода
deviceв потоковый ввод/выводIO.Stream.- warn(message)
Записывает
messageв stderr вместе с текущим стеком вызовов.- warn(message, stacktrace_info)
Записывает
messageв stderr вместе с заданнымиstacktrace_info.- write(device \\ :stdio, chardata)
Записывает
chardataв заданное устройствоdevice.
Типы
Функции
binread(device \\ :stdio, line_or_chars)Source
@spec binread(device(), :eof | :line | non_neg_integer()) :: iodata() | nodata()
Считывает данные из устройства ввода-вывода device. Операция небезопасна для Юникода.
Устройство ввода-вывода device итерируется, как указано аргументом line_or_chars.
Если
line_or_charsявляется целым числом, оно представляет количество байтов. Устройство итерируется по этому количеству байтов.Если
line_or_charsявляется:line, устройство итерируется построчно.Если
line_or_charsявляется:eof(с версии 1.13), устройство итерируется до тех пор, пока:eofне достигнет конца. Если устройство уже находится в конце, возвращается:eofсамо по себе.
Возвращает:
data- байты вывода:eof- достигнут конец файла{:error, reason}- другая (редкая) ошибка; например,{:error, :estale}при чтении с NFS-тома
Примечание: не используйте эту функцию с устройствами ввода-вывода в режиме Юникода, так как она вернет неправильный результат.
binstream()Source
@spec binstream() :: Enumerable.t(binary())
Возвращает необработанный, основанный на строках IO.Stream для :stdio. Операция небезопасна для Юникода.
Эквивалентно:
IO.binstream(:stdio, :line)
binstream(device \\ :stdio, line_or_bytes)Source
@spec binstream(device(), :line | pos_integer()) :: Enumerable.t()
Преобразует устройство ввода-вывода device в IO.Stream. Операция небезопасна для Юникода.
A IO.Stream реализует как Enumerable, так и Collectable, позволяя использовать его для чтения и записи.
Устройство ввода-вывода device итерируется по заданному количеству байтов или построчно, если задано :line. Это считывает из устройства ввода-вывода как необработанный двоичный файл.
Обратите внимание, что поток ввода-вывода имеет побочные эффекты, и каждый раз, когда вы просматриваете поток, вы можете получить разные результаты.
Не используйте эту функцию с устройствами ввода-вывода в режиме Юникода, так как она вернёт неправильный результат.
binstream/0 была добавлена в Elixir v1.12.0, а binstream/2 доступна с v1.0.0.
binwrite(device \\ :stdio, iodata)Source
@spec binwrite(device(), iodata()) :: :ok
Записывает iodata в указанное устройство device.
Эта операция предназначена для использования с «сырыми» устройствами, которые запускаются без кодировки. Заданные iodata записываются на устройство без преобразования. Дополнительную информацию об данных ввода-вывода см. в разделе «Данные ввода-вывода» в документации модуля.
Используйте write/2 для устройств с кодировкой.
Важно: не используйте эту функцию с устройствами ввода-вывода в режиме Юникода, так как она запишет неверные данные. В частности, стандартное устройство ввода-вывода по умолчанию настроено на Юникод, поэтому запись в stdio с помощью этой функции, вероятно, приведет к передаче неправильных данных.
chardata_to_string(chardata)Source
@spec chardata_to_string(chardata()) :: String.t()
Преобразует chardata в строку.
Дополнительную информацию о chardata см. в разделе «Chardata» в документации модуля.
В случае неудачи преобразования генерируется исключение UnicodeConversionError. Если передается строка, возвращается сама строка.
Примеры
iex> IO.chardata_to_string([0x00E6, 0x00DF])
"æß"
iex> IO.chardata_to_string([0x0061, "bc"])
"abc"
iex> IO.chardata_to_string("string")
"string" getn(prompt, count \\ 1)Source
@spec getn( device() | chardata() | String.Chars.t(), pos_integer() | :eof | chardata() | String.Chars.t() ) :: chardata() | nodata()
Считывает заданное количество байтов с устройства ввода-вывода :stdio.
Если устройство ввода-вывода :stdio является устройством Юникода, count означает количество точек кода Юникода, которые должны быть получены. В противном случае count — это количество исходных байтов, которые нужно получить.
См. IO.getn/3 для описания возвращаемых значений.
getn(device, prompt, count)Source
@spec getn(device(), chardata() | String.Chars.t(), pos_integer() | :eof) :: chardata() | nodata()
Считывает заданное количество байтов с устройства ввода-вывода device.
Если устройство ввода-вывода device является устройством Юникода, count означает количество точек кода Юникода, которые должны быть получены. В противном случае count — это количество исходных байтов, которые нужно получить.
Возвращает:
data- символы ввода:eof- обнаружен конец файла{:error, reason}- другая (редкая) ошибка; например,{:error, :estale}при чтении с NFS-тома
gets(device \\ :stdio, prompt)Source
@spec gets(device(), chardata() | String.Chars.t()) :: chardata() | nodata()
Считывает строку с устройства ввода-вывода device.
Возвращает:
data- символы в строке, завершённые символом перевода строки (LF) или концом файла (EOF):eof- обнаружен конец файла{:error, reason}- другая (редкая) ошибка; например,{:error, :estale}при чтении с NFS-тома
Примеры
Чтобы отобразить «Как вас зовут?» в качестве приглашения и дождаться ввода пользователя:
IO.gets("What is your name?\n") inspect(item, opts \\ [])Source
@spec inspect( item, keyword() ) :: item when item: var
Просматривает и записывает данный item в стандартный вывод.
Важно отметить, что возвращает заданный item без изменений. Это позволяет «отслеживать» значения, вставляя вызов IO.inspect/2 практически в любом месте вашего кода, например, в середине цепочки.
Включает красивую печать по умолчанию с шириной 80 символов. Ширину можно изменить, явно передав опцию :width.
Вывод можно оформить меткой, передав опцию :label для удобного отличия от других вызовов IO.inspect/2. Метка будет напечатана перед просматриваемым item.
См. Inspect.Opts для получения полного списка дополнительных опций форматирования. Для печати в другие устройства ввода-вывода см. IO.inspect/3
Примеры
IO.inspect(<<0, 1, 2>>, width: 40)
Печатает:
<<0, 1, 2>>
Мы можем использовать опцию :label для оформления вывода:
IO.inspect(1..100, label: "a wonderful range")
Печатает:
a wonderful range: 1..100
Опция :label особенно полезна в цепочках:
[1, 2, 3] |> IO.inspect(label: "before") |> Enum.map(&(&1 * 2)) |> IO.inspect(label: "after") |> Enum.sum()
Печатает:
before: [1, 2, 3] after: [2, 4, 6]
inspect(device, item, opts)Source
@spec inspect(device(), item, keyword()) :: item when item: var
Просматривает item в соответствии с заданными параметрами, используя устройство ввода-вывода device.
См. inspect/2 для полного списка параметров.
iodata_length(iodata)Source
@spec iodata_length(iodata()) :: non_neg_integer()
Возвращает размер данных ввода-вывода.
Дополнительную информацию о данных ввода-вывода см. в разделе «Данные ввода-вывода» в документации модуля.
Встроен в компилятор.
Примеры
iex> IO.iodata_length([1, 2 | <<3, 4>>]) 4
iodata_to_binary(iodata)Source
@spec iodata_to_binary(iodata()) :: binary()
Преобразует данные ввода-вывода в двоичный формат.
Операция небезопасна для Юникода.
Обратите внимание, что эта функция обрабатывает целые числа в заданных данных ввода-вывода как исходные байты и не выполняет никаких преобразований кодирования. Если вы хотите преобразовать список символов в строку UTF-8, используйте chardata_to_string/1. Дополнительную информацию о данных ввода-вывода и chardata см. в разделе «Данные ввода-вывода» в документации модуля.
Если функция получает двоичные данные, возвращается тот же двоичный файл.
Встроен в компилятор.
Примеры
iex> bin1 = <<1, 2, 3>> iex> bin2 = <<4, 5>> iex> bin3 = <<6>> iex> IO.iodata_to_binary([bin1, 1, [2, 3, bin2], 4 | bin3]) <<1, 2, 3, 1, 2, 3, 4, 5, 4, 6>> iex> bin = <<1, 2, 3>> iex> IO.iodata_to_binary(bin) <<1, 2, 3>>
puts(device \\ :stdio, item)Source
@spec puts(device(), chardata() | String.Chars.t()) :: :ok
Записывает item в указанный device, аналогично write/2, но добавляет символ новой строки в конце.
По умолчанию, device — стандартный вывод. Возвращает :ok в случае успеха.
Примеры
IO.puts("Hello World!")
#=> Hello World!
IO.puts(:stderr, "error")
#=> error read(device \\ :stdio, line_or_chars)Source
@spec read(device(), :eof | :line | non_neg_integer()) :: chardata() | nodata()
Считывает данные из device.
device итерируется в соответствии с аргументом line_or_chars:
Если
line_or_chars— целое число, оно представляет количество байтов. Устройство итерируется по этому количеству байтов.Если
line_or_charsравно:line, устройство итерируется по строкам.Если
line_or_charsравно:eof(с версии 1.13), устройство итерируется до тех пор, пока не достигнет:eof. Если устройство уже в конце, возвращает:eof.
Возвращает:
data— прочитанные символы:eof— достигнут конец файла{:error, reason}— другая (редкая) ошибка; например,{:error, :estale}при чтении с NFS-тома
stream()Source
@spec stream() :: Enumerable.t(String.t())
Возвращает основанный на строках IO.Stream для :stdio.
Эквивалентно:
IO.stream(:stdio, :line)
stream(device \\ :stdio, line_or_codepoints)Source
@spec stream(device(), :line | pos_integer()) :: Enumerable.t()
Преобразует device IO в IO.Stream.
IO.Stream реализует как Enumerable, так и Collectable, позволяя использовать его как для чтения, так и для записи.
device итерируется по заданному количеству символов или по строкам, если :line задано.
Чтение выполняется в формате UTF-8. Обратите внимание на IO.binstream/2 для обработки IO как необработанного двоичного кода.
Обратите внимание, что потоки IO обладают побочными эффектами, и каждый раз при проходе через поток вы можете получать разные результаты.
stream/0 была добавлена в Elixir v1.12.0, а stream/2 доступна с v1.0.0.
Примеры
Вот пример того, как мы эмулируем сервер эха из командной строки:
Enum.each(IO.stream(:stdio, :line), &IO.write(&1))
Еще один пример, где вы можете собирать ввод пользователя с каждой новой строки и прерывать ввод на пустой строке, а затем удалять лишние символы новой строки ("\n"):
IO.stream(:stdio, :line) |> Enum.take_while(&(&1 != "\n")) |> Enum.map(&String.replace(&1, "\n", ""))
warn(message)Source
@spec warn(chardata() | String.Chars.t()) :: :ok
Выводит message в stderr вместе с текущим стеком вызовов.
Возвращает :ok в случае успеха.
Не вызывайте эту функцию в конце другой функции. Из-за оптимизации хвостовой рекурсии запись в стек вызовов не будет добавлена, и стек вызовов будет неправильно усечен. Поэтому убедитесь, что за вызовом IO.warn/1 следует хотя бы одно выражение (или атом, такой как :ok).
Примеры
IO.warn("variable bar is unused")
#=> warning: variable bar is unused
#=> (iex) evaluator.ex:108: IEx.Evaluator.eval/4 warn(message, stacktrace_info)Source
@spec warn( chardata() | String.Chars.t(), Exception.stacktrace() | keyword() | Macro.Env.t() ) :: :ok
Выводит message в stderr вместе с указанным stacktrace_info.
stacktrace_info должно быть одним из:
списком, где все записи в стеке вызовов будут включены в сообщение об ошибке
структурой
Macro.Env(с версии 1.14.0), где будет использована одна запись из стека вызовов из среды компиляциисписком ключевых слов, содержащим как минимум опцию
:file, представляющую одну запись из стека вызовов (с версии 1.14.0). Также поддерживаются опции:line,:column,:module, и:function
Эта функция уведомляет компилятор о выводе предупреждения и генерирует диагностическое сообщение компилятора (Code.diagnostic/1). Диагностика будет содержать точную информацию о файле и местоположении, если предоставлена структура Macro.Env, или эти значения были переданы в виде списка ключевых слов, но не для стеков вызовов, так как они часто неточны.
Возвращает :ok в случае успеха.
Примеры
IO.warn("variable bar is unused", module: MyApp, function: {:main, 1}, line: 4, file: "my_app.ex")
#=> warning: variable bar is unused
#=> my_app.ex:4: MyApp.main/1 write(device \\ :stdio, chardata)Source
@spec write(device(), chardata() | String.Chars.t()) :: :ok
Записывает chardata в указанный device.
По умолчанию, device — стандартный вывод.
Примеры
IO.write("sample")
#=> sample
IO.write(:stderr, "error")
#=> error
© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.17.2/IO.html