檢視原始碼 IO (Elixir v1.16.2)

處理輸入/輸出的函式 (IO)。

這個模組中的許多函式會將 IO 裝置當作引數。IO 裝置必須是 PID 或代表程序的原子。為了方便起見,Elixir 提供 :stdio:stderr 作為 Erlang 的 :standard_io:standard_error 的捷徑。

大部分函式會使用 chardata。如果給予其他類型,函式會透過 String.Chars 協定將這些類型轉換為字串(如類型規格所示)。有關 chardata 的詳細資訊,請參閱下方的「IO 資料」區段。

IO 裝置

IO 裝置可以是原子或 PID。如果是原子,原子必須是已註冊程序的名稱。此外,Elixir 提供兩個捷徑

  • :stdio - :standard_io 的捷徑,對應到 Erlang 中目前的 Process.group_leader/0

  • :stderr - Erlang 中提供的命名程序 :standard_error 的捷徑

IO 裝置會維護其位置,這表示後續呼叫任何讀取或寫入函式都會從裝置上次存取的地方開始。可以使用 :file.position/2 函式變更檔案的位置。

IO 資料

IO 資料是一種資料類型,在某些情況下可以用作二進位資料的更有效率替代方案。

IO 資料類型的二進制或清單包含位元組(0..255 範圍內的整數)或巢狀 IO 資料。此類型是遞迴的。讓我們來看一個可能的 IO 資料範例,代表二進制 "hello"

[?h, "el", ["l", [?o]]]

內建 iodata/0 類型以 iolist/0 定義。IO 清單與 IO 資料相同,但它不允許在頂層有二進制(但二進制仍允許在清單本身)。

IO 資料的使用案例

IO 資料存在是因為你經常需要對較小的二進制區塊執行許多附加作業,才能建立較大的二進制。然而,在 Erlang 和 Elixir 中,串接二進制會將串接的二進制複製到新的二進制中。

def email(username, domain) do
  username <> "@" <> domain
end

在此函數中,建立電子郵件地址會複製 usernamedomain 二進制。現在想像你要在另一個二進制中使用結果電子郵件

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 資料可以任意巢狀,而且這是一個便宜且有效率的操作。大多數基於 IO 的 API,例如 :gen_tcpIO,會接收 IO 資料並將其直接寫入 socket,而不會將其轉換為二進制。

IO 資料的一個缺點是,你無法像對二進制一樣對 IO 資料的第一部分執行模式比對,因為你通常不知道 IO 資料的形狀。在這種情況下,你可能需要透過呼叫 iodata_to_binary/1 將其轉換為二進制,這相當有效率,因為它是以 C 原生實作的。其他功能,例如計算 IO 資料的長度,可以直接在 iodata 上計算,方法是呼叫 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 資料的「對應函式」。

如果您嘗試對 chardata 使用 iodata_to_binary/1,將會導致引數錯誤。例如,我們嘗試將無法以一個位元組表示的碼點(例如 )放入 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: π"

摘要

函式

從 IO device 中讀取。此操作不安全,可能會導致 Unicode 錯誤。

:stdio 上傳回一個未處理的、基於行的 IO.Stream。此操作不安全,可能會導致 Unicode 錯誤。

將 IO device 轉換成 IO.Stream。此操作不安全,可能會導致 Unicode 錯誤。

iodata 寫入指定的 device

將 chardata 轉換成字串。

從 IO 裝置 :stdio 取得多個位元組。

從 IO device 取得多個位元組。

從 IO device 讀取一行。

檢查指定的 item 並將其寫入裝置。

使用 IO device 根據指定的選項檢查 item

傳回 IO 資料的大小。

將 IO 資料轉換為二進位

item 寫入指定的 device,類似於 write/2,但會在結尾新增換行符號。

從 IO device 讀取資料。

:stdio 上傳回一個基於行的 IO.Stream

message 寫入 stderr,連同目前的堆疊追蹤。

message 寫入 stderr,連同指定的 stacktrace_info

chardata 寫入指定的 device

類型

@type chardata() ::
  String.t() | maybe_improper_list(char() | chardata(), String.t() | [])
@type device() :: atom() | pid()
@type nodata() :: {:error, term()} | :eof

函式

連結到此函式

binread(device \\ :stdio, line_or_chars)

檢視原始碼
@spec binread(device(), :eof | :line | non_neg_integer()) :: iodata() | nodata()

從 IO device 中讀取。此操作不安全,可能會導致 Unicode 錯誤。

device 會根據 line_or_chars 參數指定的內容進行反覆運算

  • 如果 line_or_chars 是整數,則它代表位元組數。裝置會根據該位元組數進行反覆運算。

  • 如果 line_or_chars:line,則裝置會逐行反覆運算。

  • 如果 line_or_chars:eof,則裝置會反覆運算直到 :eof。自 Elixir 1.13.0 起,line_or_chars 才可以是 :eof:eof 取代已棄用的 :all,不同之處在於 :all 會在檔案結束時傳回 "",而 :eof 則會傳回 :eof 本身。

它會傳回

  • data - 輸出位元組

  • :eof - 已達檔案結束

  • {:error, reason} - 其他(罕見)錯誤狀況;例如,如果從 NFS 卷讀取,則為 {:error, :estale}

注意:請勿在 Unicode 模式下的 IO 裝置上使用此函數,因為它會傳回錯誤的結果。

連結到此函式

binstream()

檢視原始碼 (自 1.12.0 起)
@spec binstream() :: Enumerable.t(binary())

:stdio 上傳回一個未處理的、基於行的 IO.Stream。此操作不安全,可能會導致 Unicode 錯誤。

這等同於

IO.binstream(:stdio, :line)
連結到此函式

binstream(device \\ :stdio, line_or_bytes)

檢視原始碼
@spec binstream(device(), :line | pos_integer()) :: Enumerable.t()

將 IO device 轉換成 IO.Stream。此操作不安全,可能會導致 Unicode 錯誤。

一個 IO.Stream 同時實作 EnumerableCollectable,讓它可用於讀取和寫入。

如果給定 :linedevice 會以給定的位元組數或逐行迭代。這會以原始二進位的方式從 IO 裝置讀取。

請注意,IO 串流有副作用,每次您遍歷串流時,您可能會得到不同的結果。

最後,請勿在 Unicode 模式下的 IO 裝置上使用此函數,因為它會傳回錯誤的結果。

binstream/0 已在 Elixir v1.12.0 中引入,而 binstream/2 已在 v1.0.0 中提供。

連結到此函式

binwrite(device \\ :stdio, iodata)

檢視原始碼
@spec binwrite(device(), iodata()) :: :ok

iodata 寫入指定的 device

此操作用於在未編碼啟動的「原始」裝置上。給定的 iodata 會原樣寫入裝置,不進行轉換。有關 IO 資料的更多資訊,請參閱模組文件中的「IO 資料」區段。

對於有編碼的裝置,請使用 write/2

重要:請勿在 Unicode 模式下的 IO 裝置上使用此函數,因為它會寫入錯誤的資料。特別是,標準 IO 裝置預設設定為 Unicode,因此使用此函數寫入 stdio 時,很可能會導致錯誤的資料傳送。

連結到此函式

chardata_to_string(chardata)

檢視原始碼
@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"
@spec getn(
  device() | chardata() | String.Chars.t(),
  pos_integer() | :eof | chardata() | String.Chars.t()
) :: chardata() | nodata()

從 IO 裝置 :stdio 取得多個位元組。

如果 :stdio 是 Unicode 裝置,count 表示要擷取的 Unicode 碼點數。否則,count 是要擷取的原始位元組數。

請參閱 IO.getn/3 以取得傳回值的說明。

連結到此函式

getn(device, prompt, count)

檢視原始碼
@spec getn(device(), chardata() | String.Chars.t(), pos_integer() | :eof) ::
  chardata() | nodata()

從 IO device 取得多個位元組。

如果 IO 裝置 是 Unicode 裝置,計數 表示要擷取的 Unicode 碼點數目。否則,計數 是要擷取的原始位元組數目。

它會傳回

  • 資料 - 輸入字元

  • :eof - 已達檔案結束

  • {:error, reason} - 其他(罕見)錯誤狀況;例如,如果從 NFS 卷讀取,則為 {:error, :estale}

連結到此函式

gets(device \\ :stdio, prompt)

檢視原始碼
@spec gets(device(), chardata() | String.Chars.t()) :: chardata() | nodata()

從 IO device 讀取一行。

它會傳回

  • 資料 - 以換行符號 (LF) 或檔案結束 (EOF) 結束的行中的字元

  • :eof - 已達檔案結束

  • {:error, reason} - 其他(罕見)錯誤狀況;例如,如果從 NFS 卷讀取,則為 {:error, :estale}

範例

顯示「您叫什麼名字?」作為提示並等待使用者輸入

IO.gets("What is your name?\n")
連結到此函式

inspect(item, opts \\ [])

檢視原始碼
@spec inspect(
  item,
  keyword()
) :: item
when item: var

檢查指定的 item 並將其寫入裝置。

請務必注意,它會不變地傳回給定的 項目。這使得透過在程式碼中幾乎任何地方插入 IO.inspect/2 呼叫來「監看」值,例如在管線中間。

它預設啟用漂亮列印,寬度為 80 個字元。可以透過明確傳遞 :width 選項來變更寬度。

輸出可以透過提供 :label 選項來加上標籤,以便輕鬆與其他 IO.inspect/2 呼叫區分。標籤會印在受檢查的 項目 之前。

請參閱 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)

檢視原始碼
@spec inspect(device(), item, keyword()) :: item when item: var

使用 IO device 根據指定的選項檢查 item

請參閱 inspect/2 以取得選項的完整清單。

@spec iodata_length(iodata()) :: non_neg_integer()

傳回 IO 資料的大小。

如需有關 IO 資料的更多資訊,請參閱模組文件中的 "IO 資料" 區段。

由編譯器內嵌。

範例

iex> IO.iodata_length([1, 2 | <<3, 4>>])
4
@spec iodata_to_binary(iodata()) :: binary()

將 IO 資料轉換為二進位

作業不安全 Unicode。

請注意,此函數將給定 IO 資料中的整數視為原始位元組,且不會執行任何編碼轉換。如果您想從字元清單轉換為 UTF-8 編碼字串,請改用 chardata_to_string/1。如需有關 IO 資料和字元資料的更多資訊,請參閱模組文件中的 "IO 資料" 區段。

如果此函數接收二進位檔,則會傳回相同的二進位檔。

由編譯器內嵌。

範例

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)

檢視原始碼
@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)

檢視原始碼
@spec read(device(), :eof | :line | non_neg_integer()) :: chardata() | nodata()

從 IO device 讀取資料。

如果給定 :linedevice 會逐行迭代指定數量的字元,或直到 :eof

它會傳回

  • data - 輸出的字元

  • :eof - 已達檔案結束

  • {:error, reason} - 其他(罕見)錯誤狀況;例如,如果從 NFS 卷讀取,則為 {:error, :estale}

連結到此函式

stream()

檢視原始碼 (自 1.12.0 起)
@spec stream() :: Enumerable.t(String.t())

:stdio 上傳回一個基於行的 IO.Stream

這等同於

IO.stream(:stdio, :line)
連結到此函式

stream(device \\ :stdio, line_or_codepoints)

檢視原始碼
@spec stream(device(), :line | pos_integer()) :: Enumerable.t()

將 IO device 轉換為 IO.Stream

一個 IO.Stream 同時實作 EnumerableCollectable,讓它可用於讀取和寫入。

如果給定 :linedevice 會逐行或迭代指定數量的字元。

這會以 UTF-8 從 IO 讀取。查看 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", ""))
@spec warn(chardata() | String.Chars.t()) :: :ok

message 寫入 stderr,連同目前的堆疊追蹤。

如果成功,會傳回 :ok

請勿在其他函式的尾端呼叫此函式。由於尾端呼叫最佳化,不會新增堆疊追蹤項目,而且堆疊追蹤會被不正確地修剪。因此,請確保至少有一個表達式 (或原子,例如 :ok) 接在 IO.warn/1 呼叫之後。

範例

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

warn(message, stacktrace_info)

檢視原始碼
@spec warn(
  chardata() | String.Chars.t(),
  Exception.stacktrace() | keyword() | Macro.Env.t()
) :: :ok

message 寫入 stderr,連同指定的 stacktrace_info

stacktrace_info 必須是下列其中之一

  • __STACKTRACE__,其中堆疊追蹤中的所有項目都將包含在錯誤訊息中

  • Macro.Env 結構 (自 v1.14.0 起),其中會使用編譯環境中的單一堆疊追蹤項目

  • 一個關鍵字清單,至少包含表示單一堆疊追蹤條目的 :file 選項(自 v1.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)

檢視原始碼
@spec write(device(), chardata() | String.Chars.t()) :: :ok

chardata 寫入指定的 device

預設情況下,device 是標準輸出。

範例

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

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