对于文件格式来说,表构造器提供了一种有趣的替代方式。只需在写入数据时做一点额外的工作,就能使得读取数据变得容易。这种技巧就是将数据文件写成 Lua
代码,当这写代码运行时,程序也就把数据重建了。使用表构造器时,这些代码看上去会非常像是一个普通的数据文件。
下面通过一个示例来进一步展示处理数据文件的方式。如果数据文件使用的是诸如 CSV
( comma-separated value
,逗号分割值)或 XML
等预先定义好的格式,那么我们能够选择的方法不多。不过,如果处理的是处于自身需求而创建的数据文件,那么就可以将 Lua
语言的构造器用于格式定义。此时,我们把每条数据记录表示为一个 Lua
构造器。这样,原来类似
Donald E. Knuth,Literate Programing,CSLI,1992 Jon Bentley,More Programing Pearls,Addison-Wesley,1990
的数据文件就可以改为:
Entry{ "Donald E. Knuth", "Literate Programing", "CSLI", 1992 } Entry{ "Jon Bentley", "More Programing Pearls", "Addison-Wesley", 1990 }
注意
Entry{code}
与 Entry({})
是相同的,后者以表作为唯一的参数来调用函数 Entry
。
因此,上面这段数据也是一个 Lua
程序。当需要读取该文件时,我们只需要定义一个合法的 Entry
,然后运行这个程序即可。例如,以下的代码用于计算某个数据文件中数据条目的个数:
local count = 0 function Entry () count = count + 1 end dofile("data.lua") print("number of entries: " .. count) --> number of entries: 2
下面的程序获取某个数据文件中所有作者的姓名,然后打印出这些姓名:
local authors = {} -- 保存作者姓名的集合 function Entry(b) authors[b[1]] = true end dofile("data.lua") for name in pairs(authors) do print(name) end --> Donald E. Knuth --> Jon Bentley
注意
上述的代码段中使用了事件驱动( event-driven
)的方式:函数 Entry
作为一个回调函数会在函数 dofile
处理数据文件中的每个条目时被调用。
当文件的大小并不是太大时,可以使用键值对的表示方法:
Entry{ author = "Donald E. Knuth", title = "Literate Programing", publisher = "CSLI", year = 1992 } Entry{ author = "Jon Bentley", title = "More Programing Pearls", year = 1990, publisher = "Addison-Wesley" }
这种格式是所谓的自描述数据( self-describing data
)格式,其中数据的每个字段都具有一个对应其含义的简略描述。自描述数据比 CSV
或其他压缩格式的可读性更好(至少看上去如此);同时,当需要修改时,自描述数据也易于手工编辑;此外,自描述数据还允许我们在不改变数据文件的情况下对基本数据格式进行细微的修改。例如,当我们想要增加一个新字段时,只需对读取数据文件的程序稍加修改,使其在新字段不存在时使用默认值。
使用键值对格式时,获取作者姓名的程序将变为:
local authors = {} -- 保存作者姓名的集合 function Entry(b) authors[b.author] = true end dofile("data.lua") for name in pairs(authors) do print(name) end --> Donald E. Knuth --> Jon Bentley
此时,字段的次序就无关紧要了。即使有些记录没有作者字段,我们也只需修改 Entry
函数:
function Entry (b) authors[b.author or "unknown"] = true end