引言

I/O 库提供了两套不同风格的文件处理接口。 第一种风格使用隐式的文件句柄; 它提供设置默认输入文件及默认输出文件的操作, 所有的输入输出操作都针对这些默认文件。 第二种风格使用显式的文件句柄。

当使用隐式文件句柄时, 所有的操作都由表 io 提供。 若使用显式文件句柄, io.open 会返回一个文件句柄,且所有的操作都由该文件句柄的方法来提供。

表 io 中也提供了三个 和 C 中含义相同的预定义文件句柄: io.stdin io.stdout io.stderr。 I/O 库永远不会关闭这些文件。

除非另有说明, I/O 函数在出错时都返回 nil(第二个返回值为错误消息,第三个返回值为系统相关的错误码)。 成功时返回与 nil不同的值。

1. io:open(filename [, mode])

这个函数用字符串 mode 指定的模式打开一个文件。 返回新的文件句柄。 当出错时,返回 nil加错误消息。

mode的模式如下:

模式 描述
r 以只读方式打开文件,该文件必须存在。
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
r+ 以可读写方式打开文件,该文件必须存在。
w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a+ 与a类似,但此文件可读可写
b 二进制模式,如果文件是二进制文件,可以加上b
+ 号表示对文件既可以读也可以写

当文件不存在时:

模式"r","r+",会提示错误,这两种模式不会自动创建文件。

模式"a","a+","w","w+"都会创建文件

local file = io.open("/Users/jason/Desktop/test.txt")
file:close() local file = io.open("/Users/jason/Desktop/test.txt","a")
file:close()
--会生成文件test.txt

不同模式下,分别调用读写操作

-1. Mode 为"r",文本可读,不可写

[[
创建本地文件,写入以下内容
test.txt
this is test 1
this is test 2
]]
local file = io.open("/Users/jason/Desktop/iotest.txt",'r')
print(file:write("123", "a"))
file:close() local file = io.open("/Users/jason/Desktop/iotest.txt",'r+')
print("after write",file:read("*a"))
file:close()
输出为:
nil Bad file descriptor 9
after write: test.txt
this is test 1
this is test 2

-2. Mode 为"r+",文件内容保留,新内容从文件头输入,可读

输出为:
file (0x7fff8f4f4030)
after write: 123a.txt
this is test 1
this is test 2

-3. Mode 为"w",打开文件时,会立即删除文件内容(即使不写入内容),用w模式读取的时候返回nil

输出为:
file (0x7fff8f4f4030)
after write: nil Bad file descriptor 9

-4. Mode为"w+",打开文件时,会立即删除文件内容,读时,返回空字符串

file (0x7fff8f4f4030)
after write:

-5. Mode为"a",追加模式(末尾)写入,不可读

file (0x7fff8f4f4030)
after write: nil Bad file descriptor 9 文件内容:
this is test 1
this is test 2123a

-6. Mode为"a+",追加模式写入,可读(),但是本地测试读取为空字符串-->?

file (0x7fff8f4f4030)
after write: 文件内容:
this is test 1
this is test 2123a

结论

  • 代码中我们使用了mode="r"的方式打开文件是由于这种模式要求文件必须存在,如果使用其他模式可能会创建新的文件,而不会返回失败。

  • 我们可以使用mode="r"的特性来自己实现一个检测文件是否存在的函数。

  • 注意取得函数错误信息的方法,便于查找错误原因。

-- 打开存在的文件
local file, msg = io.open("iotest.txt",'r')
if file == nil then
print(msg)
else
print("open exist file success :")
print(file)
end -- 打开不存在的文件
local ret, errormsg = io.open("iotest2.txt", "r")
print("\nopen don't exist file ret :")
print(ret)
if ret == nil then
print(errormsg)
end

2. io.close

这个函数等价于file:close(),如果省略参数file的话,那么就表示关闭默认输出文件。函数file:close()也是用来关闭文件的,只是调用形式有所不同。另外当文件描述符被垃圾回收时,对应的文件也会自动关闭,但是这个时间是不确定的。

local file = io.open("/Users/jason/Desktop/iotest.txt","r")
print("\nopen a file:")
print(file) -- 关闭打开的文件
local ret = io.close(file);
print("\nclose file ret:")
print(ret) local filenew = io.open("/Users/jason/Desktop/iotest.txt","r")
print("\n\nopen a file again:")
print(filenew) -- 换种方式再次关闭打开的文件
local retagain = filenew:close();
print("\nclose file again ret:")
print(retagain) -- 设置并打开默认输出文件
io.output("/Users/jason/Desktop/iotest.txt")
local outret = io.close()
print("\nclose default out file ret:")
print(outret) -- 设置并打开默认输入文件
io.input("/Users/jason/Desktop/iotest.txt")
local inret = io.close()
print("\nclose default in file ret:")
print(inret)
open a file:
file (0x7fff8f4f4030) close file ret:
true open a file again:
file (0x7fff8f4f4030) close file again ret:
true close default out file ret:
true
lua: /Users/jason/Desktop/opentest.lua:31: attempt to use a closed file
stack traceback:
[C]: in function 'io.close'
/Users/jason/Desktop/opentest.lua:31: in main chunk
[C]: in ?

总结

  • 保证打开文件和关闭文件成对出现是一个良好的习惯。
  • 由结果可知函数io.close(file)file:close()完全等价,但是当要关闭默认输出文件时,需要选择io.close()
  • 结果中最后一段报错原因就是io.close()只能用来关闭默认的输出文件,不能用关闭默认的输入文件,而默认的输出文件在上面已经关闭过了,如果尝试再次关闭的话,Lua 解释器就会发出报错警告。

3. io.read

从文件中读取内容,还有另一种写法就是file:read(),而题目中的写法相当于对标准输入文件的操作,也就是io.input():read()。这个函数会根据所给的格式来读取内容内容,如果读不到所指定格式的内容则会返回nil,如果不指定读取的格式,则函数会选择*l做为默认的形式读取。

可选的读取方式有:

  • *n:读取一个数字,这是唯一返回数字而不是字符串的读取格式。

  • *a:从当前位置读取余下的所有内容,如果在文件尾,则返回空串""

  • *l:读取下一个行内容,如果在文件尾部则会返回nil

  • number:读取number个字符的字符串,如果在文件尾则会返回nil,如果吧number=0,则这个函数不会读取任何内容而返回一个空串"",在文件尾返回nil

    --在文本里写入以下字串测试
    [[
    2019 11 4
    Not number
    1.this is a string
    2.this is a test
    3.the rain outside the wall
    4.over
    ]] local file = io.open("/Users/jason/Desktop/iotest.txt", "r")
    if nil == file then
    print("open file readtest.txt fail")
    end -- 读取数字
    local year = file:read("*n")
    local month = file:read("*n")
    local day = file:read("*n")
    local hour = file:read("*n") print("year = "..year)
    print("month = "..month)
    print("day = "..day)
    print("hour = "..(hour or "nil")) -- 读取行
    local content = file:read("*l")
    print("\ncontent = "..content) -- 按行读取
    local content2 = file:read("*l")
    print("content2 = "..content2) -- 读取0个字节
    local zerobyte = file:read(0)
    print("\nzerobyte = "..zerobyte) -- 读取6个字节
    local sixbyte = file:read(6)
    print("sixbyte = "..sixbyte) -- 读取所有内容
    local readall = file:read("*a")
    print("\nreadall = "..readall) -- 文件结尾读取所有内容
    local readallagain = file:read("*a")
    print("readallagain = "..readallagain) -- 文件结尾读取行
    local reademptyline = file:read("*l")
    if reademptyline == nil then
    print("\nread the end of file")
    end -- 文件尾读取0个字节
    local zerobyteagain = file:read(0)
    if zerobyteagain == nil then
    print("read the end of file")
    end file:close()
    year = 2019
    month = 11
    day = 4
    hour = nil content = Not number
    content2 = 1.this is a string zerobyte =
    sixbyte = 2.this readall = is a test
    3.the rain outside the wall
    4.over
    readallagain = read the end of file
    read the end of file

    总结

    • 使用函数要注意不同格式的读取返回nil""的情况,注意结尾的判断条件。
    • 几种不同的读取形式在文件结尾处的返回值是不同的,"*a"作用在结尾处返回空字符串"",而"*l""*n"number在结尾处返回nil

4. io.write

将每一个参数写入到文件中(言外之意可以有多个参数),但是参数的类型必须是字符串或者是数字,如果要写入其他类型则需要使用tostring(arg)函数或者string.format()函数,另外这个函数还有一种形式就是file:write(),而题目中这种形式等价于io.output():write()

-- 打开文件
local file = io.open("/Users/jason/Desktop/iotest.txt", "w")
if nil == file then
print("open file iotest.txt fail")
end -- 输入字符串
file:write("test io.write\n"); -- 输入数字
file:write(2019) -- 输入分隔符
file:write(" ") -- 继续输入数字
file:write(11)
file:write(" ")
file:write(4)
file:write("\n") -- 继续输入其他类型
file:write(tostring(os.date()))
file:write("\n")
file:write(tostring(file)) -- 关闭文件
file:close() -- 读取文件并显示
local fileread = io.open("/Users/jason/Desktop/iotest.txt", "r") local content = fileread:read("*a");
print("file content is : \n")
print(content) fileread:close()
file content is : 

test io.write
2019 11 4
Mon Nov 4 15:08:04 2019
file (0x7fff8f4f40c8)

5. io:input([file])

打开一个文件,然后把这个文件作为默认的输入文件,如果参数是一个文件名,则会以文本模式打开这个文件,并将这个文件句柄设置为默认输入文件;如果参数就是一个文件描述符,那么这个函数会把这个文件简单的设置为默认的输入文件;如果使用函数时不传参数,那么它将的返回当前的默认文件的描述符。

--文件名
io.input("/Users/jason/Desktop/iotest.txt")
print(io.read("*a"))
io.close()
-- test.txt
-- this is test 1
-- this is test 2 --文件句柄(需要使用可读模式)
local file = io.open("/Users/jason/Desktop/iotest.txt", "r")
print(io.input(file))
print(io.read("*a"))
print(file)
io.close() -- file (0x7fff8f4f40c8)
-- test.txt
-- this is test 1
-- this is test 2
-- file (0x7fff8f4f40c8)

6. io:output([file])

用文件名调用它时,(以文本模式)来打开该名字的文件, 并将文件句柄设为默认输出文件。 如果用文件句柄去调用它, 就简单的将该句柄设为默认输出文件。 如果调用时不传参数,它返回当前的默认输出文件。

在出错的情况下,函数抛出错误而不是返回错误码。

local file = io.output("/Users/jason/Desktop/iotest.txt",'a+')
print(file)
io.write("1234")
io.close() -- file (0x7fff8f4f4030)
-- 文件内容:
-- 1234

7. io.popen (prog [, mode])

在额外的进程中启动程序prog,并返回用于prog的文件句柄。通俗的来说就是使用这个函数可以调用一个命令(程序),并且返回一个和这个程序相关的文件描述符,一般是这个被调用函数的输出结果,这个文件打开模式由参数mode确定,有取值"r""w"两种,分别表示以读、写方式打开,默认是以读的方式,和os.execute()相似。

local myfile = io.popen("ls ~/Desktop/luaFile", "r")
if nil == myfile then
print("open file for ls fail")
end print("\n======commond ls result:")
-- 读取文件内容
for cnt in myfile:lines() do
print(cnt)
end -- 关闭文件
myfile:close() local secondfile = io.popen("ps aux |grep inet")
if nil == secondfile then
print("open file for ps fail")
end print("\n======commond ps result:")
-- 读取文件内容
local content = secondfile:read("*a")
print(content) -- 关闭文件
secondfile:close()

======commond ls result:
demo1.lua
demo2.lua ======commond ps result:
jason 2334 0.0 0.0 4268096 852 s003 S+ 2:16PM 0:00.00 grep inet
jason 2332 0.0 0.0 4268676 1152 s003 S+ 2:16PM 0:00.00 sh -c ps aux |grep inet

8. io.tmpfile()

返回一个临时文件的句柄,以可写(实际上也可读)的方式打开并且在程序结束时自动删除。

-- 创建并打开临时文件
local myfile = io.tmpfile()
print("\nfile handle is:")
print(myfile) -- 向文件中写入内容
myfile:write("name=Jason\n");
myfile:write("age=28\n")
myfile:write("removed file when the program ends \n") -- 做了许多操作之后
-- ...
-- ... -- 移动文件指针到开头
myfile:seek("set") -- 读取文件内容
local content = myfile:read("*a");
myfile:close(); print("\nfile content is:")
print(content)
file handle is:
file (0x7fff8f4f4030) file content is:
name=Jason
age=28
removed file when the program ends

总结

  • 注意对比函数os.tmpname()io.tmpfile()的相同点和不同点,只用时候要注意,最重要的是os.tmpname()只返回文件名,需要手动打开和关闭,而io.tmpfile()函数实现打开和关闭都是自动的。
  • io.tmpfile()函数打开的文件句柄一旦关闭就无法再打开了,所以在使用完毕之前切勿随意关闭文件。

9. io.type(obj)

检测一个文件描述符obj是否有效,假如obj是一个打开的文件描述符则会返回字符串"file",如果obj是一个关闭的文件描述符则会返回字符串"closed file",而当obj是一个无效的文件描述符时则会返回结果nil

-- 打开文件
local myfile = io.open("iotypeest.txt", "w+")
if myfile == nil then
print("open file iotypeest.txt fail")
end print("\nafter open file:")
print("myfile handle status = "..io.type(myfile)) -- 关闭文件
myfile:close() print("\nafter close file:")
print("myfile handle status = "..io.type(myfile)) -- 随便输入一个文件名
print("\nuse a error file:")
print("error file handle status = "..(io.type(errorfile) or "nil"))
after open file:
myfile handle status = file after close file:
myfile handle status = closed file use a error file:
error file handle status = nil

10. io.lines([filename])

提供一个循环迭代器以遍历文件,如果指定了文件名则当遍历结束后将自动关闭该文件;若使用默认文件,则遍历结束后不会自动关闭文件。


local contentfunc = io.lines("/Users/jason/Desktop/iotest.txt")
print("contentfunc is :")
print(contentfunc) print("\nfirst file content is :") -- 先手动调用一次
local content = contentfunc()
print(content.."\n") -- 再手动调用一次
local content = contentfunc()
print(content.."\n") -- 直接全部迭代调用
print("\nsecond file content is :")
for cnt in io.lines("/Users/jason/Desktop/iotest.txt") do
print(cnt)
end -- 使用文件描述符迭代
print("\nthird file content is :")
local myfile = io.open("/Users/jason/Desktop/iotest.txt");
for cnt in myfile:lines() do
print(cnt)
end myfile:close()
contentfunc is :
function: 0x7f852e406890 first file content is :
this is test 1 this is test 2 second file content is :
this is test 1
this is test 2 third file content is :
this is test 1
this is test 2

总结

  • 注意函数io.lines(filename)file:lines()的使用有什么不同,实际上后者在函数调用完之后并不会自动关闭文件,代码最后myfie:close()函数的调用没有报错,也说明了这个问题。
  • 由于这个函数是以读的模式打开文件的,所以要求文件必须存在,否则就会报错,所有在使用之前最好先检查一下文件是否存在。

11. io.flush

把用户程序中的缓冲区数据强制写入到文件或内存变量并清空缓冲区。`io.flush()`是作用在默认的输出文件描述符上,相当于`io.output():flush()`,对于其他的通用文件可以使用`file:flush()`或者`io.flush(file)`。

12. file:setvbuf()

- file:setvbuf (mode [, size])

- 设置输出文件缓冲区的模式,`mode`有以下三种方式可选:

  1. "full":满缓冲,冲区为空时,从流读入数据。或当缓冲区满时,向流写入数据。

  2. "line":行缓冲,每次从流中读入一行数据或向流中写入—行数据。

  3. "no":无缓冲,直接从流中读入数据或直接向流中写入数据,而没有缓冲区。

     full&line 的size可以指定缓冲的大小(按字节),忽略size将自动调整为最佳的大小

     ```lua
--问题是结果为空,并且长度是全部,看起来setbuffer 没效果?
f = io.open("/Users/jason/Desktop/he.txt","r+")
f:setvbuf("full",12)
f:write("nihaomama")
f:write("heheh")
result = f:read("*a")
print ("the result is ",result)
```

13. file:flush

向文件写入缓冲中的所有数据

14. file:lines

返回一个迭代函数,每次调用将获得文件中的一行内容,当到文件尾时,将返回nil,但不关闭文件

for line in file:lines() do
body
end

15. file:read

按指定的格式读取一个文件,按每个格式函数将返回一个字串或数字,如果不能正确读取将返回nil,若没有指定格式将指默认按行方式进行读取

*n: 读取一个数字

*a: 从当前位置读取整个文件,若为文件尾,则返回空字串

*l: [默认]读取下一行的内容,若为文件尾,则返回nil

16.#### file:write

按指定的参数格式输出文件内容,参数必须为字符或数字,若要输出其它值,则需通过tostring或string.format进行转换

17. file:seek([whence][,offset])

功能:设置和获取当前文件位置,成功则返回最终的文件位置(按字节),失败则返回nil加错误信息

whence:

  "set": 从文件头开始

  "cur": 从当前位置开始[默认]

  "end": 从文件尾开始

  offset:默认为0

当 seek 成功时,返回最终从文件开头计算起的文件的位置。 当 seek 失败时,返回 **nil** 加上一个错误描述字符串。

whence 的默认值是 "cur", offset 默认为 0 。 因此,调用 file:seek() 可以返回文件当前位置,并不改变它; 调用 file:seek("set") 将位置设为文件开头(并返回 0); 调用 file:seek("end") 将位置设到文件末尾,并返回文件大小。

```lua
--向iotest写入
[[
1.this is a string
2.this is a test
]]
local file = io.open("/Users/jason/Desktop/iotest.txt", "r+")
print(file:seek("end"))
print(file:seek("set"))
print(file:seek())
print(file:seek("cur", 10))
print(file:seek("cur"))
print(file:read(1))
print(file:seek("cur"))
file:write("123")
print(file:read("*a"))
file:close()
``` ```
输出为:
35
0
0
10
10
a
11
ring
2.this is a test
```

18. file:close()

关闭 file。 注意,文件在句柄被垃圾回收时会自动关闭, 但是多久以后发生,时间不可预期的。

Lua 学习之基础篇六<Lua IO 库>的更多相关文章

  1. Lua 学习之基础篇七<Lua Module,Package介绍>

    Lua 之Module介绍 包管理库提供了从 Lua 中加载模块的基础库. 只有一个导出函数直接放在全局环境中: [require]. 所有其它的部分都导出在表 package 中. require ...

  2. Lua 学习之基础篇四<Lua table(表)>

    table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组.字典等. Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil. Lua ta ...

  3. Lua 学习之基础篇二<Lua 数据类型以及函数库 汇总>

    引言 前面讲了运算符,这里主要对Lua的数据处理相关的数据类型和函数库进行总结归纳,后面会再接着单独分开讲解具体使用. 首先因为Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值. 值可以存储 ...

  4. Lua 学习之基础篇十<Lua 常见的语法规则>

    下面讲一些lua 常见的用法和规则,可以为学习理解lua带来帮助,最后附上的部分是lua的基本操作,基本包含所有常用语法语句. 1. if判断 lua把 nil 和false 视为"假&qu ...

  5. Lua 学习之基础篇一<Lua 运算符>

    引言 由于刚接触lua,个人觉得接触一门新语言,就要一定要对基础的部分做一个快速了解. 于是参考网上相关资料吸收并整理下来作为笔记,模糊的时候用来回顾一下. 这些部分基本都是经过自己手动测试梳理过,没 ...

  6. Lua 学习之基础篇九<Lua 协同程序(Coroutine)>

    引言 讲到协程,首先来介绍一下线程和协程的区别 lua协程和多线程 相同之处:拥有自己独立的桟.局部变量和PC计数器,同时又与其他协程共享全局变量和其他大部分东西 不同之处:一个多线程程序可以同时运行 ...

  7. Lua 学习之基础篇五<Lua OS 库>

    lua os库提供了简单的跟操作系统有关的功能 1.os.clock() 返回程序所运行使用的时间 local nowTime = os.clock() print("now time is ...

  8. Lua 学习之基础篇八<Lua 元表(Metatabble)&&继承>

    讲到元表,先看一段table的合并动作. t1 = {1,2} t2 = {3,4} t3 = t1 + t2 attempt to perform arithmetic on a table val ...

  9. Lua 学习之基础篇三<Lua 字符串操作>

    Lua字符串可以使用以下三种方式表示: 单引号间的一串字符. 双引号间的一串字符. [[和]]间的一串字符. string = [["Lua"]] print("字符串 ...

随机推荐

  1. 封装Json+日志

    /** * 输出json * @param $msg * @param int $errno */ public function printOutError($msg = '操作失败', $errn ...

  2. 路由器03---k1去广告

    HOST文件 block.hosts:https://github.com/Diumo/ADhosts 由于k1的硬件限制,8M的Flash.在没有硬改的情况下,路由无法安装太多的插件,包括众所周知的 ...

  3. 【转载】spring mvc 后端获得前端传递过来的参数的方法

    1.通过HttpServletRequest 获得 HttpServletRequest.getParameter(参数名),可以获得form表单中传递的参数,或ajax或url中传递过来的参数,如果 ...

  4. 单例模式中volatile关键字的作用

    背景&问题 在早期的JVM中,synchronized存在巨大的性能开销.因此,有人想出了一个"聪明"的技巧:双重检查锁定(Double-Checked Locking). ...

  5. Gossip协议

    Gossip数据传播协议: Fabric通过将工作负载划分到事务执行(背书和提交)对等节点和事务排序节点,优化了区块链网络性能.安全性和可伸缩性.这种网络操作的解耦需要一个安全.可靠和可伸缩的数据传播 ...

  6. poj 2406 求最短重复字串

    题解: KMP中next数组的巧妙运用.在这里我们假设这个字符串的长度是len,那么如果len可以被len-next[len]整除的话,我们就可以说len-next[len]就是那个最短子串的长度为什 ...

  7. (五)sturts2+spring整合

    一.Spring与Struts的整合 1.1:加入Spring的jar包.1.2:加入Struts的jar包.1.3:加入Struts与Spring的整合jar//struts2-spring-plu ...

  8. 加密算法 MD5 和 SHA 的 JAVA 实现

    首先先简单的介绍一下MD5 和 SHA 算法 然后看一下在  java.security.MessageDigest   (信息摘要包下) 如何分别实现  md5 加密 和 sha 加密 最后在看一下 ...

  9. CentOS7/RHEL6下,如何查看目录与子目录大小

    原文:CentOS7/RHEL6下,如何查看目录与子目录大小 通过强大的[du]命令,可以帮助我们快捷的查看目录的大小,非常实用. du命令用来查看目录或文件所占用磁盘空间的大小.常用选项组合为:du ...

  10. vue 集成 vis-network 实现网络拓扑图

    vis.js  网站 https://visjs.org/ vs  code 下安装命令 npm install vis-network 在vue  下引入 vis-network组件 const v ...