1. ホーム
  2. スクリプト・コラム
  3. ルア

Luaチュートリアル(XV)。入出力ライブラリ (I/O ライブラリ)

2022-02-13 15:42:57

I/Oライブラリでは、ファイル操作のモデルとして、シンプルモデルとフルモデルの2種類を用意しています。シンプルモデルは、現在の入力ファイルと現在の出力ファイルを想定しており、彼のI/O操作はすべてこれらのファイルに対して行われます。フルモデルでは、明示的なファイルハンドルを使用し、すべての操作をファイルハンドルのメソッドとして定義します。
    1. シンプルなモデルです。
    I/Oライブラリは、プロセスの標準入力と標準出力をデフォルトの入力ファイルと出力ファイルとして受け取ります。現在の入出力ファイルは、関数 io.input(filename) および io.output(filename) を使って変更することができます。
    1). io.write関数です。
    この関数のプロトタイプは io.write(...) です。この関数は、すべての引数を現在の出力ファイルに順次書き込みます。例えば

コピーコード コードは以下の通りです。

    io.write("hello","world") --writes out the content as helloworld

    2). io.read関数です。
    次の表は、この関数のパラメータの定義と機能説明です。

  io.read("*all") の呼び出しは、現在の入力ファイルのすべての内容を、現在の位置から読み取ります。現在の位置がファイルの終端である場合、またはファイルが空である場合、この呼び出しは空の文字列を返します。Luaは長い文字列を効率的に扱えるため、Luaでファイルからデータを丸ごと読み出し、Luaの文字列ライブラリが提供する関数で様々な処理を行うことが可能です。

    io.read("*line") を呼び出すと、現在のファイルの次の行が、改行されずに返されます。ファイルの末尾に達すると、この呼び出しはnilを返します。

コピーコード コードは以下の通りです。

 for count = 1,math.hug do
    local line = io.read("*line") -- if no arguments are passed, the default value is also "*line"
    if line == nil then
        break
    end
    io.write(string.format("%6d ",count),line,"\n")
end

    ファイル内のすべての行を反復処理したいだけなら、io.ines 関数を使って、ファイル内の各行のデータにイテレータとしてアクセスできます。

コピーコード コードは以下の通りです。

local lines = {}
for line in io.lines() do -- Access each piece of data via an iterator
    lines[#lines + 1] = line
end
table.sort(lines) -- Sort, a function provided by the table library of the Lua standard library.
for _,l in ipairs(lines) do
    io.write(l,"\n")
end

    io.read("*number") を呼び出すと、現在の入力ファイルから数値が読み込まれます。このとき、readは文字列ではなく、直接数値を返します。number"オプションは、数値の前のスペースをすべて無視し、-3, +5.2 のような数値フォーマットを処理することができます。現在読み込まれているデータが正規の数値でない場合、readはnilを返します。readの呼び出しで複数のオプションを指定することができ、関数は各オプション引数に従って適切な内容を返します。たとえば

コピーコード コードは以下の通りです。

--[[
    6.0 -3.23 1000
    ... ...
    The following code reads the numbers in the comment
--]]
while true do
    local n1,n2,n3 = io.read("*number","*number","*number","*number")
    if not n1 then
        break
    end
    print(math.max(n1,n2,n3))
end

    io.read(<num>)を呼び出すと、入力ファイルから最大n文字まで読み込み、1文字も読み込めなかった場合はnilを返します。例えば

コピーコード コードは以下の通りです。

while true do
    local block = io.read(2^13)
    if not block then
        break
    end
    io.write(block)
end

io.read(0)は、ファイルの終端に達したかどうかをチェックするための特殊なケースです。到達していない場合は空文字列を、そうでない場合はnilを返します。

2. 完全なI/Oモデル。

    LuaのフルI/Oモデルは、C言語のランタイムライブラリのファイル操作関数と非常によく似た方法で使用されており、ファイルハンドルをベースにしています。

    1). io.open 関数は指定されたファイルを開き、引数にそのファイルのオープンモードを与えます。ここで "r" は読み込み、 "w" は上書き書き込み、つまりファイルの元の内容を先に削除する、 "a" は追加書き込み、 "b" はファイルを開くためのバイナリとしてファイルを開くことを意味しています。ファイルのオープンに成功すると、この関数はそのファイルへのハンドルを返します。以後、そのファイルに基づくすべての操作は、引数としてそのハンドルを渡すことを必要とします。オープンに失敗した場合は,nil が返され,エラーメッセージは関数の第2引数で返される.
    assert(io.open(filename,mode)) -- もしオープンに失敗したら、assertは第2引数で与えられたエラーメッセージを表示します。
    2). ファイルの読み書きの関数は、読み書きをします。ここでは、例えば、コロンの構文が必要です。

コピーコード コードは以下の通りです。

 local f = assert(io.open(filename,"r"))
 local t = f:read("*all") -- for read, the arguments are exactly equivalent to those of read under the simple model.
 f:close()

    また、I/Oライブラリには、io.stdin(標準入力)、io.stdout(標準出力)、io.stderr(標準エラー出力)という3つの定義済みファイルハンドルが用意されています。例えば
    io.stderr:write("This is an error message.")
    実際には、次のようなシンプルモードとフルモードを混在させることもできます。

コピーコード コードは以下の通りです。

 local temp = io.input() --save the current file handle
 io.input("newInputfile") --opens a new input file
 io.input():close() -- closes the current file
 io.input(temp) --restores the original input file

  3). パフォーマンスハック。

    ファイル全体を一度に読む方が一行ずつ読むより高速ですが、大きなファイルでは不可能なので、Luaでは妥協策として、指定したバイト数のデータを一度に読み、現在読んでいる最後の行が完全な行でない場合は、この方法で残りの行を一緒に読み込むことにより、今回の読み込みのデータが上位論理の処理で全行であることを確認することが可能です。例えば
    local lines,rest = f:read(BUFSIZE,"*line") --rest変数には、最後の行のうち読み取れなかった部分が格納されます。

    シェルでwcコマンドを簡単に実装したものを紹介します。

コピーコード コードは以下の通りです。

local BUFSIZE = 8192
local f = io.input(arg[1]) --open input file
local cc, lc, wc, = 0, 0, 0 -- count characters, lines and words respectively   
while true do
    local lines,rest = f:read(BUFSIZE,"*line")
    if not lines then
        break
    end
    if rest then
        lines = lines ... rest ... "\n"
    end
    cc = cc + #lines
    --counting the number of words
    local _, t = string.gsub(lines."%S+","")
    wc = wc + t
    --counting the number of rows
    _,t = string.gsub(line,"\n","\n")
    lc = lc + t
end
print(lc,wc,cc)

 4). その他のファイル操作。

    io.flush 関数は io キャッシュのデータをディスクファイルにフラッシュします. io.close 関数は現在開いているファイルをクローズします.また、"cur"(デフォルト)の場合、offsetは現在の位置からの相対的なオフセット、例えば"end"の場合、ファイルの終端からの相対的なオフセットとなります。この関数の戻り値は whence パラメータに依存せず、常にファイルの現在位置、つまりファイルの先頭からの相対的なオフセット バイト数を返します。

コピーコード コードは以下の通りです。

 function fsize(file)
     local current = file:seek() -- get the current position
     local size = file:seek("end") --Get the file size
     file:seek("set",current) --restores the original current position
     return size
 end

    最後に、これらの関数はすべてnilを返し、エラーが発生した場合はエラーメッセージを返すことに注意する必要があります。