快速掌握Lua 5.3 —— I/O库 (2)

Q:什么是”Complete Model”?

A:所有的文件操作都基于明确指定的文件句柄,可以同时打开多个文件句柄。这就意味着同一时间可以操作多个文件,对于每一个文件读或写均可。文件句柄等同于C语言中的”FILE*”,它代表一个被打开文件的当前读取位置。io.open()可以指定打开的文件,并返回其文件句柄,

--[[ io.open(filename [, mode])
     以"mode"模式打开文件"filename",返回其文件句柄。
     "mode"有以下选项,功能与C语言中的"fopen()"的"mode"参数功能相同:
     "r": 以只读方式打开,文件不存在时报错(不指定时,默认使用此选项);
     "w": 以只写方式打开,若文件存在则清空文件,否则创建文件;
     "a": 以附加只写的方式打开,若文件不存在,则会创建文件,如果文件存在,
          写入的数据会被加到文件尾,即文件原先的内容会被保留(原来的EOF符保留);
     "r+": 以可读写方式打开,文件不存在时报错;
     "w+": 以可读写方式打开,若文件存在则清空文件,否则创建文件;
     "a+": 以附加可读写的方式打开,若文件不存在,则会创建文件,如果文件存在,
           写入的数据会被加到文件尾后,即文件原先的内容会被保留(原来的EOF符不保留);
     当出错时返回"nil"+错误信息+错误码。]]
print(io.open("file", "r"))    -- "file"文件不存在。
--> nil    file: No such file or directory    2
local f = io.open("file", "w+")    -- 创建"file"文件。
f:write("Hello World!")
f:close()
--[[ "file"文件中:
     Hello World!]]

print(io.open("/etc/passwd", "w"))
--> nil    /etc/passwd: Permission denied    13

Q:如何操作文件句柄?

A:之前的例子已有涉及,我们可以通过文件句柄直接调用”read”,”write”等函数,他们的功能对应各自的io.*()形式的函数,因为io.*()形式的函数实际上调用的是io.input():*()

local f = io.open("file", "w+")
--[[ 其功能等价于"io.input("file"); io.read()"。
     因为"io.read()"实际上调用的是"io.input():read()"。]]
f:read()
for l in f:lines() do     -- "file:lines()"不同于"io.lines()",在循环结束时不会关闭文件句柄。
    -- do something
end
f:write("something")
f:flush()    -- 强制将写入缓存保存到文件中。
--[[ file:setvbuf(mode [, size])
     设置写入文件的数据的缓冲模式。"mode"可以设置为以下值:
     "no": 不缓冲。写入数据直接存储到文件中。
     "full": 全缓冲。只有在缓存满或是显式的对文件调用"flush()"时,数据才写入到文件。
     "line": 行缓冲。当遇到换行时将数据写入文件(对于某些特殊文件,例如终端设备,是遇到任何输入前)。
     对于后两种模式,"size"以字节为单位指定缓冲区的大小。]]
--[[ file:seek([whence [, offset]])
     根据"whence"指定的模式,将文件当前读取位置偏移"offset"个字节。"whence"可指定的模式如下:
     "set": 将文件当前读取位置定位到文件开头。
     "cur": 将文件当前读取位置定位到当前位置。
     "end": 将文件当前读取位置定位到文件结尾。
     "whence"默认为"cur";"offset"默认为0。函数返回文件当前读取位置距文件开头的偏移。
     此函数没有对应的"io.seek()"。]]
-- 在不改变文件读取位置的情况下获取文件的大小。
function fsize(file)
    local current = file:seek()      -- get current position
    local size = file:seek("end")    -- get file size
    file:seek("set", current)        -- restore position
    return size
end
f:close()

Q:如何将一个文件的内容按照十六进制编辑器的样式输出?

A:

-- "a.lua"文件中的内容:
local f = assert(io.open(arg[1], "r"))
local block = 10    -- 每次转换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
    --[[ 十六进制与正常的字符之间的空格("+1"为的就是这些空格)。
         "%02X "的模式正好占3个字符的位置,所以最后一行如果不足10个字符,
         每个不够的字符位置使用3个空格代替。]]
    io.write(string.rep("   ", block - string.len(bytes) + 1))
    -- 控制字符都转换为了"."输出。
    io.write(string.gsub(bytes, "%c", "."), "\n")
end

-- 以脚本本身作为输入文件。
--[[ result:
     > lua a.lua a.lua
     6C 6F 63 61 6C 20 66 20 3D 20    local f =
     61 73 73 65 72 74 28 69 6F 2E    assert(io.
     6F 70 65 6E 28 61 72 67 5B 31    open(arg[1
     ...
     79 74 65 73 2C 20 22 25 63 22    ytes, "%c"
     2C 20 22 2E 22 29 2C 20 22 5C    , "."), "     6E 22 29 0A 65 6E 64 0A          n").end.
]]

附加:

1、当文件为空,或者文件当前的读取位置在文件尾时,file:read()io.read()的行为相同。file:read("a")会返回空字符串,而file:read("l")和io.read(“*number”)都会返回nil。

2、

local f = io.open("file", "r")
...    -- 执行若干次"f:read()"。
print(f:seek())    -- 打印文件当前的读取位置。
print(f:seek("end", 0))    -- 打印文件大小。

3、I/O库还提供了3个预定义的标准文件句柄,io.stdinio.stdoutio.stderr。你可以使用他们直接对标准输入、标准输出和标准错误输出读或写。

print(io.stdin:read())    -- 输入什么,打印什么。
print(io.stdout:write("123"))    -- 123
print(io.stderr:write("123"))    -- 123

4、通常在Lua中,将文件作为一个整体读取会比逐行读取要快。但有些时候,我们会面对不适合我们一次性读取的巨大的文件(比如10M或100M的文件),如果你想要在读取这类文件时达到最佳性能,你可以使用一个”chunk”大小的方式读取(例如8KB)。为了防止原文件中的一行被截断,还要在io.read()中多增加一个参数*l

--[[ "file"文件中内容:
     line1 abc
     line2
     line3 def]]

-- "a.lua"文件中内容:
local BUFSIZE = 2^13    -- 8K
local f = io.input(arg[1])    -- open input file
local cc, lc, wc = 0, 0, 0    -- char, line, and word counts
while true do
    -- 首先读取一个"chunk"的大小。为了防止一行被截断,将本行中剩余部分读完。
    local lines, rest = f:read(BUFSIZE, "*L")
    if not lines then break end
    if rest then lines = lines .. rest end    -- 把被截断的一行重新拼接起来。
    cc = cc + string.len(lines)    -- 共包含多少个字符。
    local _, t = string.gsub(lines, "%S+", "")    -- 共包含多少个单词。
    wc = wc + t
    _, t = string.gsub(lines, "\n", "\n")    -- 共包含多少行。
    lc = lc + t
end
print(lc, wc, cc)    -- 其打印结果与"wc"命令的结果相同。

--[[ result:
     > lua a.lua file
     3    5    26
     > wc file
     3    5    26    file]]

5、在类Unix的操作系统中,文本模式的文件与二进制模式的文件没有什么区别。但在Windows操作系统中可就不一样了,需要特定的标志才能正确的打开二进制文件。所以如果你的程序需要考虑跨平台的话,最好在打开二进制文件时指定”b”标志。

--[[ 一个比较实用的例子是转换Windows中的换行符"\r\n"到Unix中的换行符"\n"。
     我们不使用标准输入和标准输出,因为他们是以文本模式读取和写出数据的,
     取而代之的是使用参数指定输入输出文件。]]
local inp = assert(io.open(arg[1], "rb"))
local out = assert(io.open(arg[2], "wb"))

local data = inp:read("*a")
data = string.gsub(data, "\r\n", "\n")
out:write(data)

assert(out:close())
时间: 2024-10-22 02:46:50

快速掌握Lua 5.3 —— I/O库 (2)的相关文章

快速掌握Lua 5.3 —— 字符串库 (3)

Q:什么情况下"pattern"会匹配空串? A:要小心的使用*和-,因为它们可以匹配零次. -- 如果你打算用"%a*"匹配单词,你会发现到处都是单词. print(string.find(";$% **#$hello13", "%a*")) --> 1 0 print(string.find(";$% **#$hello13", "%a*", 6)) --> 6 5 --

快速掌握Lua 5.3 —— 编写提供给Lua使用的C库函数的技巧 (2)

Q:什么是"registry"? A:有时候,我们需要在程序中使用一些非局部的变量.在C中我们可以使用全局变量或是静态变量来实现,而在为Lua编写C库的过程中,使用以上类型的变量并不是一个好的方式.首先,这些变量中无法存储Lua的值.其次,这些变量如果在多个Lua状态机中被使用,则很可能造成非预期的结果. 一个替代方案是,将这些值存储在Lua的全局变量中.这种方式解决了上面提到的两个问题,Lua全局变量可以存储任何Lua的值,同时每一个Lua状态机都有自己独立的一套全局变量.但这依旧不

快速掌握Lua 5.3 —— 资源管理

Q:Lua的"finalizer"? A:在我们之前看到的使用"userdata"的例子中,我们只关心如何创建并使用"userdata",从未关心何时以及如何释放我们创建的"userdata",因为这些事都由Lua的垃圾回收器帮我们处理.然而很多时候,程序并不会这么简单,有可能在其中还会涉及到文件句柄,窗口句柄等,此时这些资源就需要创建者进行管理. 一些面向对象语言提供了析够器用来帮助用户管理这些资源,Lua同样提供了类似的机

Lua语言基础汇总(13)-- Lua中一些常用的库

前言 这篇文章将会来一些比较轻松的内容,就是简单的介绍一下Lua中几个常用的库.简单的说就是几个API的介绍.所以说,看起来比较容易,也没有多大的分量.就是纯粹的总结.使用库就是为了方便我们的开发,提高开发效率,同时也能保证代码的质量.希望大家以后也不要重复造轮子了. 数学库 数学库(math)由一组标准的数学函数构成.这里主要介绍几个常用的函数,其它的大家可以自行百度解决. 1. 三角函数(sin,cos,tan……) 所有的三角函数都使用弧度单位,可以用函数deg(角度)和rad(弧度)来转

[Lua]Lua调用C/C++函数/库(函数压栈方式)

test.cpp文件 /*Lua调用C/C++函数/库(函数压栈方式)*/ #include<iostream> using namespace std; #include<lua.hpp> /* 当我们需要在Lua里面调用C/C++函数时,所有的函数都必须满足以下函数签名: typedef int (*lua_CFunction) (lua_State *L);换句话说,所有的函数必须接收一个lua_State作为参数,同时返回一个整数值.因为这个函数使用Lua栈作为参数,所以它

[Lua]Lua调用C/C++函数/库(动态链接方式)

新建DLL空的win32工程,新建test.cpp文件,内容如下 /*Lua调用C/C++函数/库(动态链接方式)*/ #include<iostream> using namespace std; #include<lua.hpp> static int math_abs(lua_State *L) { lua_pushnumber(L, abs((int)luaL_checknumber(L, 1))); return 1; } static int math_cos(lua_

快速掌握Lua 5.3 —— 调试库 (2)

Q:如何调试"Closure"的"upvalue"信息? A: --[[ debug.getupvalue(f, up) 返回函数("Closure")"f"的第"up"个"upvalue"的名字和值. Lua按照"upvalues"在匿名函数中出现的顺序对其编号.如果指定的"up"索引越界,则返回"nil". 以'('开头的变

快速掌握Lua 5.3 —— 调试库 (1)

Q:什么是活动函数? A:程序中被调用但还未执行完成的函数. function g() --[[ 此时函数"g"被调用但还未执行完成,是活动函数.所以这里获取的是函数"g"的信息. "debug.getinfo(2)"获取的才是函数"f"的信息.]] local x = debug.getinfo(1, "n") for k, v in pairs(x) do print(k, v) end end fun

快速掌握Lua 5.3 —— 字符串库 (2)

Q:模式匹配字符串的相关函数? A: --[[ string.find(s, pattern [, init [, plain]]) 在字符串"s"中查找第一个与匹配模式"pattern"相匹配的子串, 函数返回子串的开始位置和终止位置.如果未找到返回"nil". 如果在"pattern"中定义了捕获,捕获物也会在之后依次返回. "init"可指定从字符串"s"的什么位置开始查找,默认为