LUA教程数据文件与持久化-47

正如我们在Section 10.1(译者:第10章Complete Examples)中看到的例子,文件格式可以通过使用Lua中的table构造器来描述。我们只需要在写数据的稍微做一些做一点额外的工作,读取数据将变得容易很多。方法是:将我们的数据文件内容作为Lua代码写到Lua程序中去。通过使用table构造器,这些存放在Lua代码中的数据可以像其他普通的文件一样看起来引人注目。

为了更清楚地描述问题,下面我们看看例子。如果我们的数据是预先确定的格式,比如CSV(逗号分割值),我们几乎没得选择。(在第20章,我们介绍如何在Lua中处理CSV文件)。但是如果我们打算创建一个文件为了将来使用,除了CSV,我们可以使用Lua构造器来我们表述我们数据,这种情况下,我们将每一个数据记录描述为一个Lua构造器。将下面的代码

Donald E. Knuth,Literate Programming,CSLI,1992
 
Jon Bentley,More Programming Pearls,Addison-Wesley,1990

写成

Entry{"Donald E. Knuth",
"Literate Programming",
"CSLI",
1992}
 
Entry{"Jon Bentley",
"More Programming Pearls",
"Addison-Wesley",
1990}

记住Entry{...}与Entry({...})等价,他是一个以表作为唯一参数的函数调用。所以,前面那段数据在Lua程序中表示如上。如果要读取这个段数据,我们只需要运行我们的Lua代码。例如下面这段代码计算数据文件中记录数:

local count = 0
function Entry (b) count = count + 1 end
dofile("data")
print("number of entries: " .. count)

下面这段程序收集一个作者名列表中的名字是否在数据文件中出现,如果在文件中出现则打印出来。(作者名字是Entry的第一个域;所以,如果b是一个entry的值,b[1]则代表作者名)

local authors = {}       -- a set to collect authors
function Entry (b) authors[b[1]] = true end
dofile("data")
for name in pairs(authors) do print(name) end

注意,在这些程序段中使用事件驱动的方法:Entry函数作为回调函数,dofile处理数据文件中的每一记录都回调用它。当数据文件的大小不是太大的情况下,我们可以使用name-value对来描述数据:

Entry{
author = "Donald E. Knuth",
title = "Literate Programming",
publisher = "CSLI",
year = 1992
}
 
Entry{
author = "Jon Bentley",
title = "More Programming Pearls",
publisher = "Addison-Wesley",
year = 1990
}

(如果这种格式让你想起BibTeX,这并不奇怪。Lua中构造器正是根据来自BibTeX的灵感实现的)这种格式我们称之为自描述数据格式,因为每一个数据段都根据他的意思简短的描述为一种数据格式。相对CSV和其他紧缩格式,自描述数据格式更容易阅读和理解,当需要修改的时候可以容易的手工编辑,而且不需要改动数据文件。例如,如果我们想增加一个域,只需要对读取程序稍作修改即可,当指定的域不存在时,也可以赋予默认值。使用name-value对描述的情况下,上面收集作者名的代码可以改写为:

local authors = {} -- a set to collect authors
function Entry (b) authors[b.author] = true end
dofile("data")
for name in pairs(authors) do print(name) end

现在,记录域的顺序无关紧要了,甚至某些记录即使不存在author这个域,我们也只需要稍微改动一下代码即可:

function Entry (b)
if b.author then authors[b.author] = true end
end

Lua不仅运行速度快,编译速度也快。例如,上面这段搜集作者名的代码处理一个2MB的数据文件时间不会超过1秒。


发布日期:

所属分类: 编程 标签:


没有相关文章!