I/O库为文件操作提供了两种不同的模型,简单模型(simple model)和完整模型(complete model)。简单模型假设有一个当前输入文件和一个当前输出文件,它的I/O操作均作用于这些文件,完整模型则使用显式的文件句柄。
1.简单I/O模型
简单I/O模型的所有操作都作用于当前两个文件。I/O库将当前输入文件初始化为进程标准输入(stdin)文件,将当前输出文件初始化为进程标准输出(stdout)文件,在执行io.read()操作时就会从标准输入中读取一行。
函数io.input和io.output可以改变这两个当前文件。io.input(filename)调用会以只读的方式打开指定的文件,并将其设为当前输入文件。以后除非再次调用io.input,否则以后所有的输入都将来源于这个文件。在输出方面,io.output也可以完成类似地工作。要想用完整模式必须使用完整模式中的io.open。
以后无论使用print还是io.write都有一个原则。即在随意编写(quick-and-dirty)的程序中,或者为调试目的而编写的代码中,提倡使用print;而在其他需要完全控制输出的地方使用io.write
print("hello","Lua"); print("Hi")
-->hello Lua
-->Hi
io.write("hello","Lua"); io.write("Hi","\n")
-->helloLuaHi
write和print有几点不同。首先,write在输出时不会添加像制表符或回车这样的额外字符。其次,write使用当前输出文件,而print总是使用标准输出文件。最后print会自动调用其参数的tostring()方法,因此它还能显示table、函数和nil。
函数io.read从当前输入文件中读取字符串,它的参数决定了要读取的数据:
"*all" | 读取整个文件 |
"*line" | 读取下一行 |
"*number" | 读取一个数字 |
<num> | 读取一个不超过<num>个字符的字符串 |
由于Lua可以高效地处理长字符串,因此在Lua中一种简单的、编写过滤器的技术就可以将整个文件读取到一个字符串中,然后处理这个字符串(通常是gsub),最后把这个字符串写到输出:
t=io.read("*all")
t=string.gsub(t,...)-->做相关的处理
io.write(t)
gsub中使用的模式可以捕获所有编码为128~255的字符及等号字符。
调用io.read("*line")会返回当前文件的下一行,但不包括换行字符,当到达文件末尾时,该调用回返回nil,以表示无后续行可返回。
在io.read的参数里面,还可以用一个数字n作为read的参数。此时,read会试着从输入文件中读取n个字符。如果读不到任何字符,它会返回nil。否则会返回一个最多n个字符的字符串。下面这个示例演示了这种读取模式,它是一种将数据从stdin复制到stdout的高效方法:
在io.read的参数里面,还可以用一个数字n作为read的参数。此时,read会试着从输入文件中读取n个字符。如果读不到任何字符,它会返回nil。否则会返回一个最多n个字符的字符串。下面这个示例演示了这种读取模式,它是一种将数据从stdin复制到stdout的高效方法:
while true do
local block=io.read(2^13) -->缓冲区大小为8K
if not block then break end
io.write(block)
end
io.read(0)是一个特殊情况,它用于检查是否到达了文件末尾。如果还有数据可以读取,它会返回一个空字符串,否则返回nil。
2.完整I/O模型
如果需要对I/O作进一步的控制,就要用到完整I/O模型。这个模型是基于文件句柄的,它等价于C语言中的流(FILE*),表示一个具有当前位置的打开文件。
打开一个文件,可以使用io.open函数,可寻参数中r代表读取,w代表写入,a代表追加,另外还有一个可选参数b代表二进制。open函数会返回表示文件的新句柄。若发生错误,则返回nil,以及一条错误消息和一个错误代码。
当打开一个文件后就可以用read/write方法进行读写了。这与read/write函数相似,但是需要用冒号语法,将它们作为文件句柄的方法来调用。如下,要打开一个文件,并读取其所有内容,可以这么做:
local f=assert(io.open(filename,"r"))
local t=f:read("*all")
f:close()
I/O库提供了3个预定义C语言流的句柄:io.stdin、io.stdout和io.stderr。这样就可以将信息写到错误流
io.stderr:write(message)
通过不指定参数调用io.input(),可以得到当前输入文件的句柄。而通过io.input(handle),可以设置当前输入文件的句柄。例如:要临时改变当前输入文件,可以这么做:
local temp=io.input() -->保存当前文件
io.input("newinput") -->打开一个新的当前文件
<对新的输入文件做一些操作>
io.input():close() -->关闭当前文件
io.input(temp) -->恢复原来的输入文件
3.其他文件操作
函数tmpfile返回一个临时文件的句柄,这个句柄是以读写方式打开。这个文件会在程序结束时自动删除。函数flush会将缓冲中的数据写入文件。它与write函数一样,将其作为一个函数调用时,io.flush()会刷新当前输出文件。而将其作为一个方法调用时,f:flush()会刷新某个特定的文件f.
函数seek可以获取和设置一个文件的当前位置。它的一般形式是f:seek(whence,offset),其中whence参数是一个字符串
,指定了如何解释offset参数,它的有效值包括“set”,offset解释为相对于文件起始的偏移量,”cur”,offset解释为相对于当前位置的偏移量。“end”,offset解释为相对于文件末尾的偏移量。
,指定了如何解释offset参数,它的有效值包括“set”,offset解释为相对于文件起始的偏移量,”cur”,offset解释为相对于当前位置的偏移量。“end”,offset解释为相对于文件末尾的偏移量。
whence的默认值是“cur”,offset的默认值是0.因此调用file:seek()不会改变文件的当前位置,并会返回当前的文件位置。调用file:seek("set")会将当前位置重置为文件的起始处并返回0.调用file:seek(“end”)会将当前位置设置到文件的末尾,并返回文件的大小。下面这个函数可以不改变文件的当前位置而获取文件的大小:
function fsize(file)
local current=file:seek() --获取当前位置
local size=file:seek("end") --获取文件大小
file:seek("set",current) --恢复位置
return size
end
-->如果有错误发生,所有这些函数都会返回nil和一条错误消息