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  
复制代码
posted @   小书臣  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
点击右上角即可分享
微信分享提示