07. i/o函数库
一. IO库分为两种模式
1. 简单模式: 设置默认的当前输入文件和一个当前输出文件,并且提供针对这些文件相关的操作,适用于简单的文件操作
2. 完整模式: 使用外部的文件句柄来实现,它以一种面对对象的形式,将所有的文件操作定义为文件句柄的方法,适用于高级文件操作,例如同时读取多个文件
二. 简单模式
--[[ file = io.input([file]) 语法: file = io.input([file]) 译文: 文件句柄 = io.input([文件句柄或文件名]) 功能: 设置默认输入文件(mode模式: "r"), 之后所有的操作都根据这个文件, 除非再次设置 参数: 参数file(可选): 1. 不填入参数获取到的是默认输入文件句柄, 如果没有设置默认输入文件则获取到的是控制台的文件io.stdin(常量保存着控制台文件句柄) 2. 填入文件句柄, 将文件句柄设置为默认输入文件, 然后返回该文件句柄 3. 填入文件名, 获取文件名句柄, 将文件句柄设置为默认输入文件, 然后返回该文件句柄 返回值: 设置成功, 返回文件句柄(file), 设置失败, 则直接报错 1. 设置文件名, 在没有找到文件名的情况下会报错 2. 在设置已关闭的文件句柄下会报错 --]] --例子1: io.input("C:/test01.txt") --填写文件名设置获取到的文件句柄为默认输入文件 --例子2: io.input() --没有填写参数获取的是例子1中设置的文件句柄 --例子3: local file = io.open("C:/test01.txt", "r") --以只读的方式打开文件, 并返回文件句柄 io.input(file) --将文件句柄设置为默认输入文件 --例子4: io.input("C:/test01.txt") --完整路径 io.input("test01.txt") --相对路径(在lua文件所在的路径中查找test01.txt文件) --[[ txt1,txt2,... = io.read(...) 语法: txt1,txt2,... = io.read([...]) 译文: 内容1, 内容2, ... = io.read([读取模式1, 读取模式2, ...]) 功能: 在默认输入文件中按指定的格式读取,每个格式函数将返回一个字串或数字,如果不能正确读取将返回nil,若没有指定格式将指默认按"*l"方式进行读取 参数: 可变长参数...(可选): 1. 不填写参数, 默认为"*l", 读取一行内容, 如果文件当前位置在文件尾返回nil 2. 填写参数, 可填写多个参数(读取模式) 模式1."*a" 从当前位置读取整个文件,若为文件尾,则返回空字串 模式2."*l" 读取一行并忽略回车换行符,当在文件末尾时,返回 nil(默认格式) 模式3."*L" 读取一行并保留回车换行符(如果存在),当在文件末尾时,返回 nil 模式4."*n" 读取一个数字,根据 Lua 的转换文法,可能返回浮点数或整数。 (数字可以有前置或后置的空格,以及符号。) 只要能构成合法的数字,这个格式总是去读尽量长的串; 如果读出来的前缀无法构成合法的数字 (比如空串,"0x" 或 "3.4e-"), 就中止函数运行,返回 nil。 模式5.num 读取一个不超过这个数量字节数的字符串,当在文件末尾时,返回 nil,如果num为零,它什么也不读,返回一个空串,当在文件末尾时,返回 nil 返回值: 格式读取成功返回读取内容, 失败返回nil, 并且不会读取后续的格式内容 --]] --例子1: io.input("C:/test01.txt") --设置默认输入文件 io.read("*a") --读取全部文件内容 io.close(io.input()) --关闭当前默认输入文件 --或者使用 io.input():close() --关闭当前默认输入文件 --例子2: io.input("C:/test01.txt") --以下两个例子是等价的 io.read("*L") --读取文件中从当前位置开始的一行内容 io.input():read("*L") --获取例子1设置的默认输入文件句柄, 然后调用读取函数 --例子3: --判断文件是否为空 local function isFileNil(filename) local file = io.input(filename) local txt = io.read(0) io.close(file) return not txt end --例子4: --一段一段的读取文件内容, 同时为了每次读取都是完整的一行 local function a(filename1, filename2) local file = io.input(filename1) io.output(filename2) while true do local txt, line = io.read(2^13,"L") --如果第一个读取模式为nil, 则后面你的模式不会读取 if not txt then break end io.write(txt, line or "") end io.close() io.close(file) end --例子5: --非 ASCII 字符(\128-\255)将被编码为“=XX”,其中 XX 是该字符值的十六进制表示,为表示一致性“=”字符同样要求被改写 local function MIMEcode(filename) --对文件进行mime编码 local file = io.input(filename) local txt = string.gsub(file:read("*a"),"[\128-\255=]", function(c) return string.format("=%02X",string.byte(c)) end) io.write(txt, "\n") end --例子6: --第一种方式: 获取文件的行号 local function fileline1(filename) local file = io.input(filename) local zl while true do local txt, linetxt = file:read(2^13, "L") if txt == nil then break end local _, l = string.gsub(txt .. (linetxt or "") , "\n" , "\n") zl = (zl or 1) + l end file:close() return zl --返回行数 end --第二种方式: 获取文件的行号 local function fileline2(filename) local file = io.input(filename) local zl = 0 while true do local txt, linetxt = file:read(2^13, "l") if txt == nil then break end txt = string.format("%s%s\n",txt, linetxt or "") local _, l = string.gsub(txt, "\n", '') zl = zl + l end file:close() return zl --返回行数 end --例子7: --实现读取指定的行号 local function Readlines(filename, line) line = line or 1 local i = 0 for v in io.lines(filename) do i = i + 1 if i == line then return v end end return nil end --例子8 --第一种方法: 获取字符串的长度(任何单个字符长度都为1) --utf8汉字占用3-4个字节, 也有的别国文字占用5-6个字节, gbk gb2312汉字占用2个字节, unicode汉字占用2个字节 local function fileLength(fileName) local iRet, sRet = pcall(function() local file = io.input(fileName) local ret = io.read("*all") io.close(file) ret = string.gsub(ret,"[\r\n]", "") local i, ascll, zs = 1, "", 0 --i表示取第几个ascll码, ascll是返回的ascll码, zs是utf8字节的总数 local arr={0,0xc0,0xe0,0xf0,0xf8,0xfc} --文字占用字节数量 while true do ascll = string.byte(ret, i) if not ascll then break end for j = #arr, 1, -1 do if ascll > arr[j] then i = i + j; break end end zs = zs + 1 end return zs end) return sRet end --第二种方法: -- 计算 UTF8 字符串的长度,每一个中文算一个字符 function utf8len(input) local len = string.len(input) --这里获取到的长度为字节数,如示例长度为:21,而我们肉眼看到的长度应该是15(包含空格) local left = len --将字节长度赋值给将要使用的变量,作为判断退出while循环的字节长度 local cnt = 0 --将要返回的字符长度 local arr = {0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc} --用来判断是否满足字节长度的列表 while left ~= 0 do --遍历每一个字符 --获取字节的ASCII值,这里的 “-” 代表反向对应的索引,-left:input反着第left --假设字符串字符input长度是:21,left的值是:21,那string.byte(input, -left)就是第一个字节的ASCII值 local tmp = string.byte(input, -left) --看上面两行 local i = #arr --获取判断列表的长度,同时作为字节长度 while arr[i] do --循环判定列表 if tmp >= arr[i] then --判定当前 “字符” 的 头“字节” ACSII值符合的范围 left = left - i --字符串字节长度 -i,也就是 减去字节长度 break --结束判断 end i = i - 1 --每次判断失败都说明不符合当前字节长度 end cnt = cnt + 1 --“字符” 长度+1 end return cnt --返回 “字符” 长度 end --例子9: --第一种方法: 按行读取内容 local function ReadLine(filename) local t = {} local file = io.input(filename) assert(file, "error: 参数(文件路径)未填写") for i = 1, math.huge do local text = io.read() if not text then break end t[#t+1] = text end io.close(file) return t end for i, j in ipairs(ReadLine("C:/test01.txt")) do io.write(string.format("%3d ",i), j, "\n") end --第二种方法: 按行读取内容 local function readLines(filename) local t = {} for linetext in io.lines(filename) do t[#t + 1] = linetext end return t end for i, j in ipairs(readLines("C:/test01.txt")) do io.write(string.format("%3d ",i), j, "\n") end --[[ local file = output([file]) 语法: local file = output([file]) 译文: local 文件句柄 = output([文件名或文件句柄]) 功能: 设置默认输出文件(mode模式: "w"), 之后所有的操作都根据这个文件, 除非再次设置, 若文件存在则清空 参数: 参数file(可选): 1. 不填入参数获取到的是默认输出文件句柄, 如果没有设置默认输出文件则获取到的是控制台的文件io.stdout(常量保存着控制台文件句柄) 2. 填入文件句柄, 将文件句柄设置为默认输出文件, 然后返回该文件句柄 3. 填入文件名, 获取文件名句柄, 将文件句柄设置为默认输出文件, 然后返回该文件句柄 返回值: 设置成功, 返回文件句柄(file), 设置失败, 则直接报错 1. 设置文件名, 在没有找到文件名的情况下会报错 2. 在设置已关闭的文件句柄下会报错 --]] --例子1: --使用此函数会将原文件清空 io.output("C:/test01.txt") --填写文件名设置获取到的文件句柄为默认输出文件 --例子2: io.output() --没有填写参数获取的是例子1中设置的文件句柄 --例子3: local file = io.open("C:/test01.txt", "w") --以只写的方式打开文件, 若文件存在则覆盖, 并返回文件句柄 io.output(file) --将文件句柄设置为默认输出文件 --例子4: io.output("C:/test01.txt") --完整路径 io.output("test01.txt") --相对路径(在lua文件所在的路径中查找test01.txt文件) --错误的例子 local file = io.output("C:/test01.txt") print( file:read("*a") ) --报错: 因为文件是以只写的方式打开的, 无法使用读功能 io.input(file) --这种方式可以, 将文件改为只读方式 file:close() --[[ local file = io.write(...) 语法: local file = io.write(...) 译文: local 文件句柄 = io.write(可变长参数, 可以多个写入内容, 用逗号隔开) 功能: 向默认输出文件写入内容 参数: 可变长参数(...): 填写多个写入内容, 仅支持数字或者字符串, 如果需要写别的类型可以使用tostring()或者string.format()函数 返回值: 写入成功, 返回文件句柄(file), 写入失败,返回nil加错误信息 --]] --例子1: local file = io.output("C:/test01.txt") file:write() --不写入值 file:close() --例子2: --写入保留4位小数的内容 io.write("sin (3) = ", math.sin(3), "\n") io.write(string.format("sin (3) = %.4f\n", math.sin(3))) --例子3: --在编写代码时应避免使用".."io.write(a..b..c)这种书写方式, 应该使用io.write(a, b, c)后者避免了串联操作, 而消耗较少的资源 io.write("123","456","789") --写入内容为: 123456789 --例子4: --io.write()与print()的区别 --[[ Write 函数与 print 函数不同在于,write 不附加任何额外的字符到输出中去,例如制表符,换行符等等。还有 write 函数是使用当前输出文件,而 print 始终使用标准输出(io.stdout)。 print函数会自动调用参数的tostring方法,所以可以显示出表(tables)函数(functions)和 nil。 --]]
三. 完全模式
--完全模式函数介绍: --[[ local file, err = io.open(filename[,mode]) 语法: local file, err = io.open(filename[,mode]) 译文: local 文件句柄 = io.open(文件名, 打开模式) 功能: 用指定的mode模式打开一个文件。 返回新的文件句柄。 当出错时,返回 nil 加错误消息 参数: 参数filename(必选): 文件名(相对路径和绝对路径) 参数mode(可选): 打开方式 "r": 只读模式, 文件不存在则返回nil加错误信息(默认模式) "w": 只写模式, 文件存在则清空, 文件不存在则新建 "a": 追加模式, 文件存在则在文件尾追加要写入的内容, 文件不存在则新建 "r+": 读写模式, 文件不存在则返回nil加错误信息 "w+": 读写模式, 文件存在则清空, 文件不存在则新建, 会不会有疑问? 清空了还怎么读? "a+": 读写模式, 文件存在则在文件尾追加要写入的内容, 文件不存在则新建 "b", 二进制模式打开, 配合上面的模式进行使用, 用于图片, exe, apk非文本文件的复制 二进制将数据在内存中的样子原封不动的搬到文件中,文本格式则是将每一个数据转换成字符写入到文件中,他们在大小上,布局上都有着区别 由此可以看出,2进制文件可以从读出来直接用,但是文本文件还多一个“翻译”的过程,因此2进制文件的可移植性好 正常文本是把二进制内容通过编码转换文字的 非文本文件, 比如电脑上一些exe文件之类的, 这种才采用二进制编辑 返回值: file, 返回新的文件句柄。 当出错时,返回 nil 加错误消息 --]] --例子1: --以面向对象的方式调用 local file, err = io.open("C:/test01.txt","w") file:write() file:close() --例子2: --将windos模式下的文本修改成unix模式下的文本 --就是将“回车换行字符”替换成“换行字符” --使用open不能同时打开同一个文件 local file1 = assert(io.open("C:/test01.txt", "rb")) local data = file1:read("*all") data = string.gsub(data, "\r\n", "\n") file1:close() local file2 = assert(io.open("C:/test01.txt", "wb")) file2:write(data) file2:close() --例子3: --按照10字节为一段读取文件,将每一段各字节的十六进制表示显示出来。接着再以文本的形式写出该段,并将控制字符转换为点号。 local f = assert(io.open("C:/test01.txt", "rb")) local block = 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 io.write(string.rep(" ", block - string.len(bytes) + 1)) io.write(string.gsub(bytes, "%c", "."), "\n") end --例子4(二进制复制文件):复制一些非文本文件用二进制读写可以避免文件损坏 local function copyFile(filename1, filename2) return pcall(function() local Rfile = io.open(filename1, "rb") local Wfile = io.open(filename2, "wb") local text while true do text = Rfile:read(2^13) --每次读取8KB文件, 防止一次性读取("*all")造成内存不足 if text == nil then break end Wfile:write(text) end Rfile:close() Wfile:close() end) end --问题1: --file:seek设置当前位置, 对于追加模式无效 --问题2: --设置默认输入输出文件和open可以混用
四. 其它文件操作
--[=[ local pos = io.seek([whence[,offset]]) 语法: local pos = io.seek([whence[,offset]]) 也可以写成: local pos = f:seek([whence[, offset]]) 译文: local 当前位置 = io.seek(从什么位置开始, 偏移多少位置) 功能: 获取和设置文件的当前位置 参数: 参数whence(可选): "set": 表示在开头位置开始, 0 "cur": 表示在当前位置开始(默认) "end": 表示在结束位置开始 参数offset(可选):设置偏移值 返回值: 返回指针当前所在位置 --]=] --例子1: f:seek() --不以任何参数调用该函数,该函数返回流在文件中的当前指针位置 f:seek("set") --会将指针位置重置到文件开头并返回0 f:seek("end") --会将位置重置到文件结尾并返回文件的大小(最后的指针位置) --例子2: function fsize(file) local current = file:seek() -- 保存当前流偏移位置 local size = file:seek("end") -- 获取文件大小 file:seek("set", current) -- 恢复当前位置 return size end --[=[ local file = io.tmpfile() 语法: local file = io.tmpfile() 译文: local 文件句柄 = io.tmpfile() 功能: 返回一个临时文件句柄, 这个文件以"w+"模式打开,在临时目录创建一个文件, 在程序结束时会自动删除文件 参数: 参数whence(可选): "set": 表示在开头位置开始, 0 "cur": 表示在当前位置开始(默认) "end": 表示在结束位置开始 参数offset(可选):设置偏移值 返回值: 返回指针当前所在位置 --]=]
1. 下面两个命令不知道啥意思, 有啥用, 百度也没有找到示例
五. io库扩展
--IO库扩展 --检查指定的文件是否存在,如果存在返回 true,否则返回 false --%1: 要检查的文件名 local function isFile(filename) local file = io.open(type(filename) == "string" and filename or "") if file then file:close() end return not not file end --读取文件内容,返回包含文件内容的字符串,如果失败返回 nil --会一次性读取整个文件的内容,并返回一个字符串,因此该函数不适宜读取太大的文件 --%1: 要检查的文件名 local function readFile(filename) filename = type(filename) == "string" and filename or "" local file, text = io.open(filename) if file then text = file:read("*all") file:close() end return text end --写入文件内容 --"写入模式" 参数最后追加字符 "b" ,表示以二进制方式写入数据,这样可以避免内容写入不完整。 --在 Android 平台上,文件只能写入存储卡所在路径,assets 和 data 等目录都是无法写入的。 function writeFile(path, content, mode) mode = mode or "w+b" local file = io.open(path, mode) if file then if file:write(content) == nil then return false end io.close(file) return true else return false end end --拆分一个路径字符串,返回组成路径的各个部分 local function pathInfo(path) end --返回指定文件的大小,如果失败返回 false function io.filesize(path) local size = false local file = io.open(path, "r") if file then local current = file:seek() size = file:seek("end") io.close(file) end return size end
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了