chapter 12_1 数据文件

时间:2021-06-21 20:47:44

  Lua的一个重要特性,就是可以作为配置文件,利用到table构造式来定义一种文件格式。

只需要在写数据时做一点额外的工作,读取数据就会变得相当容易。也就是将数据作为Lua代码输出。

当运行这些代码时,程序也就读取了数据。而table的构造式可以使这些输出代码看上去更像是一个普通的数据文件。

如果是为了应用而创建数据文件的话,那么就可以使用Lua的构造式作为格式。在这种格式中,每条数据记录表示为

一个Lua构造式:

local mode = {}
function Module(b)
if b.name then
mode[b.name] = b
end
end dofile("data") ---[[ data 文件
Module{
name = "video",
frame_rate = ,
frame_type = "H264",
frame_bitrate = ,
frame_interval = ,
frame_control = "vbr", -- cbr
} Module{
name = "audio",
audio_type = "acc",
sampling_rate = , --采样率
audio_channel = "stereo", --立体声
}

Module{<code>}与Module({<code>})是完全等价的,都是以一个table作为参数来调用Module函数。

因此,上面的这段数据也是一个Lua程序。为了读取该文件,我们只需要定义一个合适的Module,然后运行就可以了。

这种格式就是“自描述的数据”格式,其中每项数据都伴随一个表示其含义的简短描述。比CSV或其他紧缩格式更具可读性。

当需要修改时,可以在基本格式中作一个细小的改动,而不需要同时改变数据文件。

例如要新增一个字段,只需修改读取程序中的一小块就可以了,内容就是当该字段不存在时提供一个默认值。

使用名值对格式,可以打印收集mode的程序:

for name in pairs(mode) do
print (name)
end

Lua不仅运行速度快,而且编译速度也快。

自从Lua创建之处就把数据描述作为Lua的主要应用之一来考虑。

串行化

  通常需要串行化一些数据,也就是将数据转换为一个字节流或字符流。然后就可以将其存储到一个文件中,或者通过

网络连接发送出去。串行化后的数据可以用Lua代码来表示,这样当运行这些代码时,存储的数据就可以在读取程序中得到重构。

  如果想要恢复一个全局变量的值,那么串行化的结果或许可以是"varname = <exp>",其中<exp>是一段用于创建该值得Lua代码。

而varname只是一个简单的标识符。

下面演示如何编写创建一个值的代码,例如对于一个数字值,方法如下:

function serialize(o)
if type(o) == "number" then
io.write(string.format("%a",o))
else
<other cases>
end
end

对于一个字符串值,方法如下:

if type(o) == "string" then
io.write("'",o,"'")

如果字符串包括特殊字符(引号或者换行符),那么最终代码就不是一段有效的Lua程序了。

但是可以使用另一种方法:

if type(o) == "string" then
io.write("[[ ",o,"]] ")

注意,如果恶意的用户故意使其字符串为" ]] .. os.execute('rm *').. [[ ",那么最终保存下来的结果变成:

varname = [[ ]] .. os.execute('rm *').. [[]]

如果加载这个数据,将会出现不可估量的后果。

一个简单的办法:用“%q”来使用string.format函数。这样它就会用双引号来括住字符串,并且正确地转移其中的双引号和换行符。

a = 'a "problematic" \\string'
print(string.format("%q",a)) --> "a \"problemmatic\" \\string"

通过这个特性,serialize函数可以改为:

function serialize(o)
if type(o) == "number" then
io.write(o)
elseif type(o) == "string" then
io.write(string.format("%q",o))
else
<other cases>
end
end

Lua5.1还提供了另一种可以以一种安全的方法来括住任意字符串的方法。

用“[=[ ... ]=]” 标记长字符串。这种方式主要是为手写的代码提供方便的,通过它就不需要改变任何字符串的内容了。

在自动生成的代码中,要转移那些问题字符,还是使用string.format与“%q”选项更为方便。