この第1版は Lua 5.0 向けに書かれています。後のバージョンにもほぼ当てはまりますが、いくつかの違いがあります。
第4版は Lua 5.3 を対象としており、Amazon やその他の書店で入手できます。
本書を購入することで、Lua プロジェクトの支援にもなります。


21.1 – シンプルな入出力モデル

シンプルなモデルは、2つのカレントファイルに対してすべての操作を行います。ライブラリは、カレント入力ファイルをプロセスの標準入力(stdin)として、カレント出力ファイルをプロセスの標準出力(stdout)として初期化します。したがって、io.read() のようなものを実行すると、標準入力から1行読み込みます。

これらのカレントファイルは、io.input 関数と io.output 関数で変更できます。 io.input(filename) のような呼び出しは、指定されたファイルを読み込みモードで開き、カレント入力ファイルとして設定します。この時点から、io.input が再度呼び出されるまで、すべての入力はこのファイルから行われます。 io.output は出力に対して同様の動作をします。エラーが発生した場合、両方の関数はエラーを発生させます。エラーを直接処理したい場合は、完全モデルの io.open を使用する必要があります。

writeread よりも単純なので、まずこちらを見てみましょう。 io.write 関数は、任意の数の文字列引数を受け取り、カレント出力ファイルに書き込みます。数値は通常の変換規則に従って文字列に変換されます。この変換を完全に制御するには、string ライブラリの format 関数を使用する必要があります。

    > io.write("sin (3) = ", math.sin(3), "\n")
      --> sin (3) = 0.1411200080598672
    > io.write(string.format("sin (3) = %.4f\n", math.sin(3)))
      --> sin (3) = 0.1411
io.write(a..b..c) のようなコードは避けてください。 io.write(a,b,c) の呼び出しは、連結を避けるため、より少ないリソースで同じ効果を実現します。

原則として、手軽なプログラムやデバッグには print を使用し、出力内容を完全に制御する必要がある場合は write を使用する必要があります。

    > print("hello", "Lua"); print("Hi")
      --> hello   Lua
      --> Hi
    
    > io.write("hello", "Lua"); io.write("Hi", "\n")
      --> helloLuaHi
print とは異なり、write はタブや改行などの余分な文字を出力に追加しません。さらに、write はカレント出力ファイルを使用しますが、print は常に標準出力を使用します。最後に、print は引数に自動的に tostring を適用するため、テーブル、関数、nil も表示できます。

read 関数は、カレント入力ファイルから文字列を読み込みます。引数によって、何が読み込まれるかが制御されます。

"*all"ファイル全体を読み込みます
"*line"次の行を読み込みます
"*number"数値を読み込みます
num最大 *num* 文字の文字列を読み込みます

io.read("*all") の呼び出しは、現在の位置から開始して、カレント入力ファイル全体を読み込みます。ファイルの末尾にある場合、またはファイルが空の場合、呼び出しは空の文字列を返します。

Lua は長い文字列を効率的に処理するため、Lua でフィルタを記述する簡単な手法は、ファイル全体を文字列に読み込み、文字列に対して処理を行い(通常は gsub を使用)、文字列を出力に書き込むことです。

    t = io.read("*all")         -- read the whole file
    t = string.gsub(t, ...)     -- do the job
    io.write(t)                 -- write the file
例として、次のコードは、MIME の *quoted-printable* エンコーディングを使用してファイルの内容をエンコードする完全なプログラムです。このエンコーディングでは、ASCII 以外の文字は =*XX* としてエンコードされます。ここで、*XX* は16進数での文字の数値コードです。エンコーディングの整合性を保つために、「=」文字もエンコードする必要があります。 gsub で使用されるパターンは、128 から 255 までのコードを持つすべての文字と、等号をキャプチャします。
    t = io.read("*all")
    t = string.gsub(t, "([\128-\255=])", function (c)
          return string.format("=%02X", string.byte(c))
        end)
    io.write(t)
Pentium 333MHz では、このプログラムは 200K 文字のファイルを変換するのに 0.2 秒かかります。

io.read("*line") の呼び出しは、カレント入力ファイルから次の行を改行文字なしで返します。ファイルの末尾に達すると、呼び出しは nil を返します(返す次の行がないため)。このパターンは read のデフォルトであるため、io.read()io.read("*line") と同じ効果があります。通常、このパターンは、アルゴリズムがファイルを行ごとに自然に処理する場合にのみ使用します。そうでない場合は、後で説明するように、*all を使用してファイル全体を一度に読み込むか、ブロック単位で読み込むことをお勧めします。このパターンの使用例として、次のプログラムはカレント入力をカレント出力にコピーし、各行に番号を付けます。

    local count = 1
    while true do
      local line = io.read()
      if line == nil then break end
      io.write(string.format("%6d  ", count), line, "\n")
      count = count + 1
    end
ただし、ファイル全体を行ごとに反復処理するには、io.lines イテレータを使用する方が適切です。たとえば、ファイルの行をソートする完全なプログラムは次のように記述できます。
    local lines = {}
    -- read the lines in table 'lines'
    for line in io.lines() do
      table.insert(lines, line)
    end
    -- sort
    table.sort(lines)
    -- write all the lines
    for i, l in ipairs(lines) do io.write(l, "\n") end
このプログラムは、4.5 MB(32K 行)のファイルを 1.8 秒でソートします(Pentium 333MHz)。C で記述され高度に最適化されたシステムの sort プログラムでは 0.6 秒かかります。

io.read("*number") の呼び出しは、カレント入力ファイルから数値を読み込みます。これは、read が文字列ではなく数値を返す唯一のケースです。ファイルから多くの数値を読み取る必要がある場合、中間文字列がないことでパフォーマンスが大幅に向上する可能性があります。 *number オプションは、数値の前にあるスペースをスキップし、-3+5.21000-3.4e-23 などの数値形式を受け入れます。現在のファイル位置に数値が見つからない場合(形式が正しくないか、ファイルの末尾のため)、nil を返します。

read は複数のオプションを指定して呼び出すことができます。引数ごとに、関数はそれぞれの結果を返します。1行あたり3つの数値を含むファイルがあるとします。

    6.0       -3.23     15e12
    4.3       234       1000001
    ...
ここで、各行の最大値を出力したいとします。 read を1回呼び出すだけで、3つの数値すべてを読み取ることができます。
    while true do
      local n1, n2, n3 = io.read("*number", "*number",
                                 "*number")
      if not n1 then break end
      print(math.max(n1, n2, n3))
    end
いずれの場合も、io.read からオプション "*all" を使用してファイル全体を読み込み、次に gfind を使用して分割するという代替案を常に検討する必要があります。
    local pat = "(%S+)%s+(%S+)%s+(%S+)%s+"
    for n1, n2, n3 in string.gfind(io.read("*all"), pat) do
      print(math.max(n1, n2, n3))
    end

基本的な読み取りパターンの他に、数値 *n* を引数として read を呼び出すことができます。この場合、read は入力ファイルから *n* 文字を読み取ろうとします。文字を読み取れない場合(ファイルの末尾)、readnil を返します。それ以外の場合、最大 *n* 文字の文字列を返します。この読み取りパターンの例として、次のプログラムは、stdin から stdout にファイルをコピーする効率的な方法です(もちろん Lua で)。

    local size = 2^13      -- good buffer size (8K)
    while true do
      local block = io.read(size)
      if not block then break end
      io.write(block)
    end

特別な場合として、io.read(0) はファイルの末尾のテストとして機能します。読み取るべきものが残っている場合は空の文字列を返し、そうでない場合は nil を返します。