第一版是為 Lua 5.0 編寫的。儘管對後續版本來說仍然有很大的關聯性,但仍有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買本書,您也可以 支援 Lua 專案


21.1 – 簡單的 I/O 模型

簡單的模型對兩個目前的檔案執行所有操作。函式庫將目前的輸入檔案初始化為程序的標準輸入(stdin),將目前的輸出檔案初始化為程序的標準輸出(stdout)。因此,當我們執行類似 io.read() 的程式時,我們會從標準輸入讀取一行。

我們可以使用 io.inputio.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 是字元的十六進位數字代碼。為了保持編碼的一致性,=´ 字元也必須編碼。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 上,此程式花費 0.2 秒來轉換一個有 200K 字元的檔案。

呼叫 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
此程式會在 1.8 秒內對一個有 4.5 MB(32K 行)的檔案進行排序(在 Pentium 333MHz 上),而系統 sort 程式會花費 0.6 秒,而該程式是用 C 編寫且經過高度最佳化。

呼叫 io.read("*number") 會從目前的輸入檔案中讀取一個數字。這是 read 會傳回數字而不是字串的唯一情況。當您需要從檔案中讀取許多數字時,沒有中間的字串會大幅提升效能。*number 選項會跳過數字前的任何空白,並接受 -3+5.21000-3.4e-23 等數字格式。如果它在目前的檔案位置找不到數字(因為格式錯誤或檔案結束),它會傳回 nil

您可以使用多個選項呼叫 read;對於每個引數,函式會傳回對應的結果。假設您有一個檔案,每行有三個數字

    6.0       -3.23     15e12
    4.3       234       1000001
    ...
現在您想要列印每一行的最大值。您可以在單一呼叫 read 中讀取所有三個數字
    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 個字元。如果它無法讀取任何字元(檔案結束),read 會傳回 nil;否則,它會傳回一個最多有 n 個字元的字串。以下程式是一個有效率的方式(當然是在 Lua 中)來將檔案從 stdin 複製到 stdout,作為這個讀取模式的範例

    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