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.stdin
,io.stdout
和io.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())
时间: 2024-10-22 02:46:50