第一版是為 Lua 5.0 編寫的。儘管對後續版本來說仍然有很大的關聯性,但仍有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買本書,您也可以 支援 Lua 專案。
![]() |
用 Lua 程式設計 | ![]() |
第三部分。標準函式庫 第 21 章。I/O 函式庫 |
簡單的模型對兩個目前的檔案執行所有操作。函式庫將目前的輸入檔案初始化為程序的標準輸入(stdin
),將目前的輸出檔案初始化為程序的標準輸出(stdout
)。因此,當我們執行類似 io.read()
的程式時,我們會從標準輸入讀取一行。
我們可以使用 io.input
和 io.output
函式變更那些目前的檔案。類似 io.input(filename)
的呼叫會開啟指定的檔案(以讀取模式),並將其設定為目前的輸入檔案。從此之後,所有輸入都會來自這個檔案,直到下一個對 io.input
的呼叫;io.output
對輸出執行類似的任務。如果發生錯誤,兩個函式都會引發錯誤。如果您想直接處理錯誤,您必須使用來自完整模型的 io.open
。
由於 write
比 read
簡單,我們會先檢視它。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.2
、1000
和 -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。
版權所有 © 2003–2004 Roberto Ierusalimschy。保留所有權利。 | ![]() |