Spec-Zone.ru › Elixir 1.10

IO

Функции обработки ввода/вывода (IO).

Многие функции в этом модуле ожидают устройство ввода/вывода в качестве аргумента. Устройство ввода/вывода должно быть PID или атомом, представляющим процесс. Для удобства Elixir предоставляет :stdio и :stderr в качестве сокращений для :standard_io и :standard_error Erlang.

Большинство функций ожидают данные типа chardata. В случае, если задан другой тип, функции преобразуют эти типы в строку с помощью протокола String.Chars (как показано в описании типов). Для получения дополнительной информации о 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":

[?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 представляют кодовые точки Unicode. Байты (byte/0) — это целые числа в диапазоне 0..255, а кодовые точки Unicode (char/0) — целые числа в диапазоне 0..0x10FFFF. Модуль IO предоставляет функцию chardata_to_string/1 для chardata как "аналог" функции iodata_to_binary/1 для данных IO.

Если вы попытаетесь использовать iodata_to_binary/1 с chardata, это приведет к ошибке аргумента. Например, давайте попробуем поместить кодовую точку, которую нельзя представить одним байтом, например, ?π, внутри данных IO:

iex> 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: π"

Резюме

Типы

chardata()
device()
nodata()

Функции

binread(device \\ :stdio, line_or_chars)

Читает из устройства ввода/вывода device. Операция не безопасна с точки зрения Unicode.

binstream(device, line_or_bytes)

Преобразует устройство ввода/вывода device в IO.Stream. Операция не безопасна с точки зрения Unicode.

binwrite(device \\ :stdio, iodata)

Записывает iodata в указанное устройство device.

chardata_to_string(string)

Преобразует 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(device, line_or_codepoints)

Преобразует устройство ввода/вывода device в IO.Stream.

warn(message)

Записывает message в stderr вместе со текущим стеком вызовов.

warn(message, stacktrace)

Записывает message в stderr вместе с указанным stacktrace.

write(device \\ :stdio, chardata)

Записывает chardata в указанное устройство device.

Типы

chardata()

Specs

chardata() ::
  String.t() | maybe_improper_list(char() | chardata(), String.t() | [])

device()

Specs

device() :: atom() | pid()

nodata()

Specs

nodata() :: {:error, term()} | :eof
END_OF_DOCUMENT_MARKER

Функции

binread(device \\ :stdio, line_or_chars)

Характеристики

binread(device(), :all | :line | non_neg_integer()) :: iodata() | nodata()

Считывает данные из Ввода-Вывода device. Операция не поддерживает Unicode.

Итерация device осуществляется заданным количеством байтов или по строкам, если задано :line. В противном случае, если задано :all, возвращается весь device.

Возвращает:

  • data - выходные байты

  • :eof - достигнут конец файла

  • {:error, reason} - другая (редкая) ошибка; например, {:error, :estale} при чтении с NFS-тома

Если задано :all, то :eof никогда не возвращается, а вместо этого возвращается пустая строка в случае достижения EOF устройством.

Примечание: не используйте эту функцию с устройствами Ввода-Вывода в режиме Unicode, так как она вернёт неправильный результат.

binstream(device, line_or_bytes)

Характеристики

binstream(device(), :line | pos_integer()) :: Enumerable.t()

Преобразует Ввод-Вывод device в IO.Stream. Операция не поддерживает Unicode.

Объект IO.Stream реализует как Enumerable, так и Collectable, что позволяет использовать его для чтения и записи.

Итерация device выполняется заданным количеством байтов или по строкам, если задано :line. Это считывает из устройства Ввода-Вывода как сырой бинарный данные.

Обратите внимание, что поток Ввода-Вывода имеет побочные эффекты, и при каждом прохождении по потоку вы можете получить разные результаты.

Не используйте эту функцию с устройствами Ввода-Вывода в режиме Unicode, так как она вернёт неправильный результат.

binwrite(device \\ :stdio, iodata)

Характеристики

binwrite(device(), iodata()) :: :ok | {:error, term()}

Записывает iodata в заданный device.

Эта операция предназначена для использования с "сырыми" устройствами, которые запускаются без кодировки. Заданные iodata записываются в устройство как есть, без преобразования. Дополнительную информацию об данных Ввода-Вывода см. в разделе "Данные Ввода-Вывода" в документации модуля.

Используйте write/2 для устройств с кодировкой.

Важно: не используйте эту функцию с устройствами Ввода-Вывода в режиме Unicode, так как она запишет неправильные данные. В частности, стандартное устройство Ввода-Вывода по умолчанию настроено на Unicode, поэтому запись в stdio с помощью этой функции, вероятно, приведет к отправке по каналу неправильных данных.

chardata_to_string(string)

Характеристики

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)

Характеристики

getn(chardata() | String.Chars.t(), pos_integer()) :: chardata() | nodata()
getn(device(), chardata() | String.Chars.t()) :: chardata() | nodata()

Считывает заданное количество байтов с устройства Ввода-Вывода :stdio.

Если :stdio является устройством Unicode, count подразумевает количество точек кода Unicode для извлечения. В противном случае, count - это количество байтов для извлечения.

См. IO.getn/3 для описания значений возвращаемых результатов.

getn(device, prompt, count)

Характеристики

getn(device(), chardata() | String.Chars.t(), pos_integer()) ::
  chardata() | nodata()

Считывает заданное количество байтов с устройства Ввода-Вывода device.

Если устройство Ввода-Вывода device является устройством Unicode, count подразумевает количество точек кода Unicode для извлечения. В противном случае, count - это количество байтов для извлечения.

Возвращает:

  • data - введённые символы

  • :eof - достигнут конец файла

  • {:error, reason} - другая (редкая) ошибка; например, {:error, :estale} при чтении с NFS-тома

gets(device \\ :stdio, prompt)

Характеристики

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 \\ [])

Характеристики

inspect(item, keyword()) :: item when item: var

Проверяет и записывает заданный item на устройство.

Важно отметить, что он возвращает заданный item без изменений. Это позволяет «просматривать» значения, вставляя вызов IO.inspect/2 практически в любом месте кода, например, в середине конвейера.

По умолчанию он поддерживает красивую печать с шириной 80 символов. Ширина может быть изменена, если явно указать опцию :width.

Выходные данные могут быть снабжены меткой, передав опцию :label для легкой идентификации вызовов IO.inspect/2. Метка будет напечатана перед проверяемым item.

См. Inspect.Opts для полного списка оставшихся параметров форматирования.

Примеры

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)

Характеристики

inspect(device(), item, keyword()) :: item when item: var

Проверяет item в соответствии с заданными параметрами, используя устройство Ввода-Вывода device.

См. inspect/2 для полного списка параметров.

iodata_length(iodata)

Характеристики

iodata_length(iodata()) :: non_neg_integer()

Возвращает размер данных Ввода-Вывода.

Дополнительную информацию о данных Ввода-Вывода см. в разделе "Данные Ввода-Вывода" в документации модуля.

Встроен в компилятор.

Примеры

iex> IO.iodata_length([1, 2 | <<3, 4>>])
4

iodata_to_binary(iodata)

Характеристики

iodata_to_binary(iodata()) :: binary()

Преобразует данные Ввода-Вывода в двоичные.

Операция не поддерживает Unicode.

Обратите внимание, что эта функция обрабатывает целые числа в заданных данных Ввода-Вывода как сырые байты и не выполняет никакого преобразования кодировки. Если вы хотите преобразовать список символов в строку 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)

Характеристики

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)

Характеристики

read(device(), :all | :line | non_neg_integer()) :: chardata() | nodata()

Считывает данные из устройства Ввода-Вывода device.

Итерация device выполняется заданным количеством символов или по строкам, если задано :line. В противном случае, если задано :all, возвращается вся device.

Возвращает:

  • data - выходные символы

  • :eof - достигнут конец файла

  • {:error, reason} - другая (редкая) ошибка; например, {:error, :estale} при чтении с NFS-тома

Если задано :all, то :eof никогда не возвращается, а вместо этого возвращается пустая строка в случае достижения EOF устройством.

stream(device, line_or_codepoints)

Характеристики

stream(device(), :line | pos_integer()) :: Enumerable.t()

Преобразует ввод-вывод device в IO.Stream.

IO.Stream реализует как Enumerable, так и Collectable, что позволяет использовать его для чтения и записи.

device итерируется указанным количеством символов или построчно, если задано :line.

Чтение выполняется в формате UTF-8. Обратите внимание на IO.binstream/2 для обработки ввода-вывода как сырого двоичного.

Обратите внимание, что поток ввода-вывода имеет побочные эффекты, и каждый раз при проходе по потоку вы можете получать разные результаты.

Примеры

Вот пример того, как мы имитируем эхо-сервер из командной строки:

Enum.each(IO.stream(:stdio, :line), &IO.write(&1))

warn(message)

Характеристики

warn(chardata() | String.Chars.t()) :: :ok

Записывает message в stderr вместе со текущим стеком вызовов.

Возвращает :ok в случае успеха.

Примеры

IO.warn("variable bar is unused")
#=> warning: variable bar is unused
#=>   (iex) evaluator.ex:108: IEx.Evaluator.eval/4

warn(message, stacktrace)

Характеристики

warn(chardata() | String.Chars.t(), Exception.stacktrace()) :: :ok

Записывает message в stderr вместе с указанным stacktrace.

Эта функция также уведомляет компилятор о выводе предупреждения (в случае включения --warnings-as-errors). Возвращает :ok в случае успеха.

Для предотвращения вывода стека вызовов можно передать пустой список.

Примеры

stacktrace = [{MyApp, :main, 1, [file: 'my_app.ex', line: 4]}]
IO.warn("variable bar is unused", stacktrace)
#=> warning: variable bar is unused
#=>   my_app.ex:4: MyApp.main/1

write(device \\ :stdio, chardata)

Характеристики

write(device(), chardata() | String.Chars.t()) :: :ok

Записывает chardata в указанный device.

По умолчанию device — стандартный вывод.

Примеры

IO.write("sample")
#=> sample

IO.write(:stderr, "error")
#=> error

© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.10.4/IO.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API