快速掌握Lua 5.3 —— I/O庫 (2)


Q:什么是”Complete Model”?

A:所有的文件操作都基於明確指定的文件句柄,可以同時打開多個文件句柄。這就意味着同一時間可以操作多個文件,對於每一個文件讀或寫均可。文件句柄等同於C語言中的”FILE*”,它代表一個被打開文件的當前讀取位置。io.open()可以指定打開的文件,並返回其文件句柄,

--[[ io.open(filename [, mode])
以"mode"模式打開文件"filename",返回其文件句柄。
"mode"有以下選項,功能與C語言中的"fopen()"的"mode"參數功能相同:
"r": 以只讀方式打開,文件不存在時報錯(不指定時,默認使用此選項);
"w": 以只寫方式打開,若文件存在則清空文件,否則創建文件;
"a": 以附加只寫的方式打開,若文件不存在,則會創建文件,如果文件存在,
寫入的數據會被加到文件尾,即文件原先的內容會被保留(原來的EOF符保留);
"r+": 以可讀寫方式打開,文件不存在時報錯;
"w+": 以可讀寫方式打開,若文件存在則清空文件,否則創建文件;
"a+": 以附加可讀寫的方式打開,若文件不存在,則會創建文件,如果文件存在,
寫入的數據會被加到文件尾后,即文件原先的內容會被保留(原來的EOF符不保留);
當出錯時返回"nil"+錯誤信息+錯誤碼。]]

print(io.open("file", "r")) -- "file"文件不存在。
--> nil file: No such file or directory 2
local f = io.open("file", "w+") -- 創建"file"文件。
f:write("Hello World!")
f:close()
--[[ "file"文件中:
Hello World!]]


print(io.open("/etc/passwd", "w"))
--> nil /etc/passwd: Permission denied 13

Q:如何操作文件句柄?

A:之前的例子已有涉及,我們可以通過文件句柄直接調用”read”,”write”等函數,他們的功能對應各自的io.*()形式的函數,因為io.*()形式的函數實際上調用的是io.input():*()

local f = io.open("file", "w+")
--[[ 其功能等價於"io.input("file"); io.read()"。
因為"io.read()"實際上調用的是"io.input():read()"。]]

f:read()
for l in f:lines() do -- "file:lines()"不同於"io.lines()",在循環結束時不會關閉文件句柄。
-- do something
end
f:write("something")
f:flush() -- 強制將寫入緩存保存到文件中。
--[[ file:setvbuf(mode [, size])
設置寫入文件的數據的緩沖模式。"mode"可以設置為以下值:
"no": 不緩沖。寫入數據直接存儲到文件中。
"full": 全緩沖。只有在緩存滿或是顯式的對文件調用"flush()"時,數據才寫入到文件。
"line": 行緩沖。當遇到換行時將數據寫入文件(對於某些特殊文件,例如終端設備,是遇到任何輸入前)。
對於后兩種模式,"size"以字節為單位指定緩沖區的大小。]]

--[[ file:seek([whence [, offset]])
根據"whence"指定的模式,將文件當前讀取位置偏移"offset"個字節。"whence"可指定的模式如下:
"set": 將文件當前讀取位置定位到文件開頭。
"cur": 將文件當前讀取位置定位到當前位置。
"end": 將文件當前讀取位置定位到文件結尾。
"whence"默認為"cur""offset"默認為0。函數返回文件當前讀取位置距文件開頭的偏移。
此函數沒有對應的"io.seek()"。]]
-- 在不改變文件讀取位置的情況下獲取文件的大小。
function fsize(file)
local current = file:seek() -- get current position
local size = file:seek("end") -- get file size
file:seek("set", current) -- restore position
return size
end
f:close()

Q:如何將一個文件的內容按照十六進制編輯器的樣式輸出?

A:

-- "a.lua"文件中的內容:
local f = assert(io.open(arg[1], "r"))
local block = 10 -- 每次轉換10個字符,作為一行顯示。
while true do
local bytes = f:read(block)
if not bytes then break end
for b in string.gmatch(bytes, ".") do -- 匹配任意字符。
-- 將字符轉換為十六進制的形式。
io.write(string.format("%02X ", string.byte(b)))
end
--[[ 十六進制與正常的字符之間的空格("+1"為的就是這些空格)。
"%02X "的模式正好占3個字符的位置,所以最后一行如果不足10個字符,
每個不夠的字符位置使用3個空格代替。]]
io.write(string.rep(" ", block - string.len(bytes) + 1))
-- 控制字符都轉換為了"."輸出。
io.write(string.gsub(bytes, "%c", "."), "\n")
end

-- 以腳本本身作為輸入文件。
--[[ result:
> lua a.lua a.lua
6C 6F 63 61 6C 20 66 20 3D 20 local f =
61 73 73 65 72 74 28 69 6F 2E assert(io.
6F 70 65 6E 28 61 72 67 5B 31 open(arg[1
...
79 74 65 73 2C 20 22 25 63 22 ytes, "%c"
2C 20 22 2E 22 29 2C 20 22 5C , "."), "\
6E 22 29 0A 65 6E 64 0A n"
).end.
]]

附加:

1、當文件為空,或者文件當前的讀取位置在文件尾時,file:read()io.read()的行為相同。file:read("a")會返回空字符串,而file:read("l")和io.read(“*number”)都會返回nil。
2、

local f = io.open("file", "r")
... -- 執行若干次"f:read()"
print(f:seek()) -- 打印文件當前的讀取位置。
print(f:seek("end", 0)) -- 打印文件大小。

3、I/O庫還提供了3個預定義的標准文件句柄,io.stdinio.stdoutio.stderr。你可以使用他們直接對標准輸入、標准輸出和標准錯誤輸出讀或寫。

print(io.stdin:read())    -- 輸入什么,打印什么。
print(io.stdout:write("123")) -- 123
print(io.stderr:write("123")) -- 123

4、通常在Lua中,將文件作為一個整體讀取會比逐行讀取要快。但有些時候,我們會面對不適合我們一次性讀取的巨大的文件(比如10M或100M的文件),如果你想要在讀取這類文件時達到最佳性能,你可以使用一個”chunk”大小的方式讀取(例如8KB)。為了防止原文件中的一行被截斷,還要在io.read()中多增加一個參數*l

--[[ "file"文件中內容:
line1 abc
line2
line3 def]]


-- "a.lua"文件中內容:
local BUFSIZE = 2^13 -- 8K
local f = io.input(arg[1]) -- open input file
local cc, lc, wc = 0, 0, 0 -- char, line, and word counts
while true do
-- 首先讀取一個"chunk"的大小。為了防止一行被截斷,將本行中剩余部分讀完。
local lines, rest = f:read(BUFSIZE, "*L")
if not lines then break end
if rest then lines = lines .. rest end -- 把被截斷的一行重新拼接起來。
cc = cc + string.len(lines) -- 共包含多少個字符。
local _, t = string.gsub(lines, "%S+", "") -- 共包含多少個單詞。
wc = wc + t
_, t = string.gsub(lines, "\n", "\n") -- 共包含多少行。
lc = lc + t
end
print(lc, wc, cc) -- 其打印結果與"wc"命令的結果相同。

--[[ result:
> lua a.lua file
3 5 26
> wc file
3 5 26 file]]

5、在類Unix的操作系統中,文本模式的文件與二進制模式的文件沒有什么區別。但在Windows操作系統中可就不一樣了,需要特定的標志才能正確的打開二進制文件。所以如果你的程序需要考慮跨平台的話,最好在打開二進制文件時指定”b”標志。

--[[ 一個比較實用的例子是轉換Windows中的換行符"\r\n"到Unix中的換行符"\n"。
我們不使用標准輸入和標准輸出,因為他們是以文本模式讀取和寫出數據的,
取而代之的是使用參數指定輸入輸出文件。]]

local inp = assert(io.open(arg[1], "rb"))
local out = assert(io.open(arg[2], "wb"))

local data = inp:read("*a")
data = string.gsub(data, "\r\n", "\n")
out:write(data)

assert(out:close())

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: