Lua数据文件和序列化

文章新地址
    在处理数据文件时,写数据通常比读数据简单很多。当向一个文件中写时,我们拥有绝对的控制权;但是,当从一个文件中读时,我们并不知道会读什么东西。一个健壮的程序除了能够处理一个合法文件中所包含的所有类型的数据外,还应该能够优雅地处理错误的文件。因此,编写一个健壮的处理输入的程序总是比较困难的。
    Lua语言自1993年发布以来,其主要用途之一就是描述数据。在那个年代,主要的文本数据描述语言之一是SGML。对于很多人来说,SGML既臃肿又复杂。在1998年,有些人将其简化成XML,但以我们的眼光看仍然臃肿又复杂。有些人跟我们的观点一直,进而在2001年开发了JSON。JSON基于JavaScript,类似于一种精简过的Lua语言数据文件。一方面,JSON的一大优势在于它是国际标准,包括Lua语言在内的多种语言都具有操作JSON文件的标准库。另一方面,Lua语言数据文件的读取更加容易和灵活。
    使用一门全功能的编程语言来描述数据确实非常灵活,但也会带来两个问题。问题之一在于安全性,这是因为“数据”文件能够肆意地在我们的程序中运行。我们可以通过沙盒中运行程序来解决这个问题。
    另一个问题是性能问题。Lua语言不仅运行得快,编译也很快。例如,在笔者的新机器上,Lua5.3可以在4秒以内,占用240MB内存,完成1000万条赋值语句的读取、编译和运行。作为对比,Perl5.18需要21秒、占用6GB内存,Python2.7和Python3.4直接崩溃,Node.js0.10.25在运行8秒后抛出“内存溢出”异常,Rhino1.7在运行6分钟后也抛出了“内存溢出”异常。

数据文件

    对于文件格式来说,表构造器提供了一种有趣的替代方法。只需在写入数据时做一点额外的工作,就能使得读数据变得容易。这种技巧就是将数据文件写成Lua代码,当这些代码运行时,程序也就把数据重建了。使用表构造器时,这些代码段看上去会非常像是一个普通的数据文件。
    下面通过一个示例来进一步展示处理数据文件的方式。如果数据文件使用的是诸如CSV或XML等预先定义好的格式,那么我们能够选择的方法不多。不过,如果处理的是处于自身需求而创建的数据文件,那么就可以将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{code}与Entry({code})是相同的,后者以表作为唯一的参数来调用函数Entry。因此,上面这段数据也是一个Lua程序。当需要读取该文件时,我们只需要定义一个合法的Entry,然后运行这个程序即可。例如,以下代码用于计算某个数据文件中数据条目的个数:

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

下面的程序获取某个数据文件中所有作者的姓名,然后打印出这些姓名:

local authors = {
   }		-- 保存作者姓名的集合
function Entry (b) authors[b[1]] = true end
dofile("data")
for name in pairs(authors) do print(name) end

请注意,上述的代码段中使用了事件驱动的方式:函数Entry作为一个回调函数会在函数dofile处理数据文件中的每个条目时被调用。
    当文件的大小并不是太大时,可以使用键值对的表示方法:

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

这种格式是所谓的自描述数据格式,其中数据的每个字段都具有一个对应其含义的简略描述。自描述数据比CSV或其他压缩格式的可读性更好;同时,当需要修改时,自描述数据也已于手工编辑;此外,自描述数据还允许我们在不改变数据文件的情况下对基本数据格式进行细微的修改。例如,当我们想要增加一个新字段时,只需要对读取数据文件的程序稍加修改,使其在新字段不存在时使用默认值。
    此时,字段的次序就无关紧要了。即使有些记录没有作者字段,我们也只需要修改Entry函数:

function Entry(b)
	authors[b.author or "unknown"] = true
end

序列化

    我们常常需要将某些数据序列化/串行化,即将数据转换为字节流动或字符流,以便将其存储到文件中或者通过网络传输。我们也可以将序列化后的数据表示为Lua代码,当这些代码运行时,被序列化的数据就可以在读取程序中得到重建。
    通常,如果想要恢复一个全局变量的值,那么可能会使用形如varname = exp这样的代码。其中,exp是用于创建这个值的Lua代码,而varname是一个简单的标识符。接下来,让我们学习如何编写创建值的代码。例如,对于一个数值类型而言,可以简单地使用如下代码:


                
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平淡风云

您的打赏是我继续创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值