Lua学习笔记 第二十一章 I/O库

I/O库为文件操作提供了两种不同的模型,简单模型(simple model)和完整模型(complete

model)。简单模型假设有一个当前输入文件和一个当前输出文件,它的I/O操作均作用于

这些文件。完整模型则使用显示的文件句柄。它采用了面向对象的风格,并将所有的操作

定义为文件句柄上的方法。

21.1 简单I/O模型

简单模型的所有操作都作用于两个当前文件。I/O库将当前输入文件初始化为进程标准输入,

将当前输出文件初始化为进程标准输出。在执行io.read()时,就出从标出输入中读取一行。

函数io.input和io.output可以改变这两个当前文件。io.input(filename)调用会以只读模式打开

指定文件,并将其设为当前输入文件;在输出方面,io.output也可以完成类似的工作;如果

出现错误,这两个函数都会引发错误。如果想直接处理这些错误,则必须使用完整模型中的

io.open.

函数io.write可以接受任意数量的字符串参数,并将它们写入当前输出文件。它也可以接受数

字参数,数字参数会根据常规的转换规则转换为字符串。如果想要控制这种转换,则应该使

用函数string.format.

write 与 print的几点不同:

首先,write在输出时不会添加像制表符或回车这样的额外字符;

其次,write使用当前输出文件,而print总是使用标准输出;

最后,print会自动调用其参数的tostring()方法,因此它还能显示table、函数和nil。

函数io.read从当前输入文件中读取字符串,它的参数决定了要读取的数据:

"*all"      读取整个文件

"*line"     读取下一行

"*number"   读取一个数字

<num>       读取一个不超过<num>个字符的

字符串调用io.read("*all")会读取当前输入文件的所有内容,以当前位置作为开始。

如果当前位置处于文件的末尾,或者文件为空,那么该调用会返回一个空字符串;

调用io.read("*line")会返回当前文件的下一行,但并不包括换行符。当到达文件末尾

时,该调用会返回nil,以表示无后续行可返回。它也是read的默认模式。建议使用

"*all"一次性读取整个文件,或者按块来读取。如果只是为了迭代文件中的所有行,

那么io.lines迭代器更为合适。

for line in io.lines() do lines[#lines+1] =line end

调用io.read("*number")会从当前输入文件中读取一个数字。此时,read会返回一个数字,

而不是字符串。"*number"选项会忽略数字前面的所有空格,并且能处理像-3、+5.2、100

及-3.4e-23这样的数字格式。如果无法在当前文件位置读到一个数字,read会返回nil。

在调用read时可以指定多个选项,函数会根据每个选项参数返回相应的内容。

示例——有一个文件,其中每行有3个数字。现在要打印出每一行中最大的数字。

方法一,用一次read函数调用来读取每行的3个数字:

while true do

local n1, n2, n3 =io.read("*number", "*number", "*number")

    if not n1thenbreak end

    print(math.max(n1,n2, n3))

end

方法二,用"*all"来读取整个文件,然后用gmatch来提取其中内容:

local pat = "(%S+)%s+(%S)%s+(%S)%s+"

for n1,n2, n3 in string.gmatch(io.read("*all"),pat) do

    print(math.max(tonumber(n1),tonumber(n2), tonumber(n3)))

end

除以上的读取模式外,还可以用一个数字n作为read的参数。此时,read会试着从输入

文件中读取n个字符。如果读不到任何字符,它会返回nil。否则会返回一个最多n个字符

的字符串。

io.read(0)是一个特殊的情况,它用于检测是否达到了文件末尾。如果还有数据可以读取,

它会返回一个空字符串,否则返回nil。

 

21.2 完整I/O模型

完整模型是基于文件句柄的,它等价于c语言中的流,表示一个具有当前位置的打开文件;

要打开一个文件可以使用io.open函数,它有两个参数一个是要打开的文件名,另一个是

模式字符串。模式字符串:

"r"表示读取;"w"表示写入; "a"表示追加; "b"表示打开二进制文件。

open函数会返回表示文件的新句柄。若发生错误,则返回nil,及一条错误消息和一个错误代码。

一个错误检查的典型做法是;

local f = assert(io.open(filename, mode))

如果打开失败,错误消息就会成为assert的第二个参数,然后assert会显示这个信息。

打开文件后,就可以用read/write方法读写文件了。但要使用冒号语法,将它们作为文件

句柄的方法来调用。--> 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),可以设置当前输入文件的句柄。

 

21.2.1 性能诀窍

在读取大文件时为了避免在行中间断开,只需在读一个块是再加上一行:

local lines, rest = f:read(BUFSIZE, "*line")

变量rest包含了被块所断开的那行的剩余部分。这样就可以将块与行的剩余部分连接起来,

从而得到一个总是起止于行边界上的块。

示例——统计文件中字符数、单词数和行数的程序:

local BUFSIZE = 2^13 --8k

local f = io.input(arg[1])  -- 打开输入文件

local cc, ic, wc = 0, 0, 0

while true do

    local lines,rest = f:read(BUFSIZE, "*line")

    if not linesthen break end

    if rest thenlines = lines .. rest .. "\n" end

    cc = cc +#lines    -- 行数

    local _, t =string.gsub(lines, "%S+", "")

    wc = wc + t         --单词数

    _, t =string.gsub(lines, "\n", "\n")

    lc = lc + t         --换行符数

end

print(lc, wc, cc)

 

21.2.3 其它文件操作

函数tmpfile会返回一个临时文件的句柄,这个句柄是以读/写方式打开。这个文件会在程序结束时自动删除。

函数flush会将缓冲中的数据写入文件。它与write函数一样,将其作为一个函数调用时,

io.flush()会刷新当前输出文件;而将其作为一个方法调用时,f:flush()会刷新某个特定的文件f。

函数seek可以获取和设置一个文件的当前位置。它的一半形式是f:seek(whence,offset),

其中whence参数是一个字符串,指定了如何解释offset参数。它的有效值包括:

"set",offset解释为相对于文件起始的偏移量;

"cur",offset解释为相对于当前位置的偏移量;

"end",offset解释为相对于文件末尾的偏移量;

函数的返回值,总是返回文件的当前位置相对于文件起始处的偏移字节数。

whence参数的默认值是"cur",offset的默认值是0。因此调用file:seek()不会改变文件的

当前位置,并返回当前的文件位置。调用file:seek("set")会将当前位置重置为文件的起始

处并返回0.调用file:seek("end")会将当前位置设置到文件的末尾,并返回文件的大小。

如果发生错误,所有这些函数都会返回nil和一条错误消息。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值