Lua学习笔记 第十二章 数据文件与持久性
2014-08-25 17:44
393 查看
12.1
数据文件
像10.1节介绍的那样,可以由table构造式来定义一种文件格式。这项技术也就是将数据
作为Lua代码来输出,当运行这些代码时,程序也就读取了数据。示例——原来的数据文件格式:
Donald E.Knuth, Literate Programming, CSLI, 1992
Jon Bentley, More Programming Pearls, Addisom-Wesley,1990
现在可以改写为:
Entry{"Donald E.Knuth",
"Literate Programming",
"CSLI",
1992}
Entry{"Jon Bentley",
"MoreProgramming Pearls",
"Addison-Wesley",
1950}
注意: Entry{<code>}
与Entry({<code>}) 是完全等价的,都是以一个table作为参数来调用函数Entry。
为了读取该文件我们只需定义一个合适的Entry,然后运行此程序就可以了。
以下这个程序计算了数据文件中条目的数量:
local count = 0
function Entry(_) count = count + 1 end
dofile("data")
-- 文件data里的数据就是上面Entry里面的数据
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,
publisher ="Addison-Wesley"}
这样收集作者姓名的程序改为:
local authors = {}
function Entry(b)
if b.author then authors[b.author] = true end
end
dofile("data")
for name in pairs(authors) do print(name) end
12.2
串行化
串行化数据(serialization)就是将数据转换为一个字节流或字符流。然后就可以将其存储到一个文件中,
或者通过网络连接发送出去。
串行化后的数据可以用Lua
4000
代码来表示,这样当运行这些代码时,存储的数据就可以在读取程序中得到重构。
如果要恢复一个全局变量的值,那么串行化的结果或许可以是"varname =<exp>",其中<exp>是一段用于创建
该值的Lua代码,varname只是一个简单的标示符。示例:
function serialize(o)
if type(o) =="number" then
io.write(o)
elseif type(o) == "string" then
io.write(string.format("%q",o))
else
<其它情况>
end
end
string.format("%q", str)
函数会用双引号来括住字符串,并且正确地转移其中的双引号和换行符等其它特殊字符。
此外Lua5.1
还提供了另外一种可以以一种安全的方法来括住任意字符串的方法。这种新的标记方式[=[ ... ]=],
用于长字符串。然而这种新方式主要是为手写的代码提供方便的,通过它就不需要改变任何字符串的内容。
但在自动生成的代码中还是推荐使用string.format("%q", str),更方便.在使用这种方法时要注意两个细节问题。
首先,必须使用正确数量的等号。这个正确的数量应比字符串中出现的最长的等号序列还大1。
其次,Lua总是会忽略所有长字符串开头的换行符,避免这个问题的简单方法就是在字符串起始处添加一个换行符。
以下quote函数就是根据这两个注意点编写的处理函数:
function quote(s) --
查找最长的等号序列
local n = -1
for w instring.gmatch(s, "]=*") do
n =math.max(n, #w - 1)
end
--
产生n+1个等号
local eq =string.rep("=", n+1)
--
生成长字符串的字面表示,在字符串开始的地方加了一个换行符
returnstring.format("[%s[\n%s]%s]", eq, s, eq)
end
12.2.1
保存无环的table
下一个任务是保存table。保存table的方法有几种,选用哪种方法取决于table的结构。简单的table
不仅需要简单的算法,而且更需要完美地输出结果。
第一个算法如下:
function serialize(o)
if type(o) =="number" then
io.write(o)
elseiftype(o) == "string" then
io.write(string.format("%q",o))
elseiftype(o) == "table" then
io.write("{\n")
for k, vin pairs(o) do
io.write("", k, " = ")
serialize(v)
io.write(",\n")
end
io.write("}\n")
else
error("cannotserialize a " .. type(o))
end
end
上面函数假设了一个table中的所有key都是合法的标示符。但如果一个table的key为数字或者为非法
的Lua标识符,那么就会出现问题。一个简单的解决方法是将这行代码:
io.write(" ", k, " = ")改为:
io.write(" ["); serialize(k);io.write("] = ")
这样便增强了这个函数的强健性,但却损失了结果文件的美观性。例如对于调用:
serialize{a=12, b='Lua', key='another"one"'}
第一个版本的serialize会输出:
{
a = 12,
b ="Lua",
key ="another \"one\"",
}
而第二个版本则输出:
{
["a"]= 12,
["b"]="Lua",
["key"]= "another \"one\"",
}
12.2.2
保存有环的table
如果要处理任意拓扑结构(带环的table或共享子table)的table,就需要采用另外一种方法。
保存函数要求将待保存的值及其名称一起作为参数传入;此外还必须持有一份所有已保持过的table的
名称记录,以此来检测环并复用其中的table。
代码如下:
function basicSerialize(o)
if type(o) =="number" then
return tostring(o)
else --
假设 o 是字符串
returnstring.format("%q", o)
end
end
functin save(name, value saved)
saved = savedor {}
--初始值
io.write(name,"=")
iftype(value) == "number" or type(value) == "string" then
io.write(basicSerialize(value),"\n")
elseiftype(value) == "table" then
if saved(value) then
-- 该value是否已经保存过?
io.write(saved[value], "\n")
-- 使用先前的名字
else
saved[value] = name
-- 为下次使用保持名字
io.write("{}\n")
-- 创建一个新的table
for k, v in pairs(value) do
-- 保存其字段
k = basicSerialize(k)
local fname = string.format("%s[%s]",name, k)
save(fname,v, saved)
end
end
else
error("cannotsave a ".. type(value))
end
end
示例——假设有一个table如下所示:
a = {x=1, y=2, {3,4,5}}
a[2] = a
-- 环
a.z = a[1]
-- 共享子table
调用save("a", a)
保存的结果为:
a = {}
a[1] = {}
a[1][1] = 3
a[1][2] = 4
a[1][3] = 5
a[2] = a
a["y"] = 2
a["x"] = 1
a["z"] = a[1]
如果想以共享的方式来保存几个table中的共同部分,只需调用save时使用相同的saved
参数。
数据文件
像10.1节介绍的那样,可以由table构造式来定义一种文件格式。这项技术也就是将数据
作为Lua代码来输出,当运行这些代码时,程序也就读取了数据。示例——原来的数据文件格式:
Donald E.Knuth, Literate Programming, CSLI, 1992
Jon Bentley, More Programming Pearls, Addisom-Wesley,1990
现在可以改写为:
Entry{"Donald E.Knuth",
"Literate Programming",
"CSLI",
1992}
Entry{"Jon Bentley",
"MoreProgramming Pearls",
"Addison-Wesley",
1950}
注意: Entry{<code>}
与Entry({<code>}) 是完全等价的,都是以一个table作为参数来调用函数Entry。
为了读取该文件我们只需定义一个合适的Entry,然后运行此程序就可以了。
以下这个程序计算了数据文件中条目的数量:
local count = 0
function Entry(_) count = count + 1 end
dofile("data")
-- 文件data里的数据就是上面Entry里面的数据
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,
publisher ="Addison-Wesley"}
这样收集作者姓名的程序改为:
local authors = {}
function Entry(b)
if b.author then authors[b.author] = true end
end
dofile("data")
for name in pairs(authors) do print(name) end
12.2
串行化
串行化数据(serialization)就是将数据转换为一个字节流或字符流。然后就可以将其存储到一个文件中,
或者通过网络连接发送出去。
串行化后的数据可以用Lua
4000
代码来表示,这样当运行这些代码时,存储的数据就可以在读取程序中得到重构。
如果要恢复一个全局变量的值,那么串行化的结果或许可以是"varname =<exp>",其中<exp>是一段用于创建
该值的Lua代码,varname只是一个简单的标示符。示例:
function serialize(o)
if type(o) =="number" then
io.write(o)
elseif type(o) == "string" then
io.write(string.format("%q",o))
else
<其它情况>
end
end
string.format("%q", str)
函数会用双引号来括住字符串,并且正确地转移其中的双引号和换行符等其它特殊字符。
此外Lua5.1
还提供了另外一种可以以一种安全的方法来括住任意字符串的方法。这种新的标记方式[=[ ... ]=],
用于长字符串。然而这种新方式主要是为手写的代码提供方便的,通过它就不需要改变任何字符串的内容。
但在自动生成的代码中还是推荐使用string.format("%q", str),更方便.在使用这种方法时要注意两个细节问题。
首先,必须使用正确数量的等号。这个正确的数量应比字符串中出现的最长的等号序列还大1。
其次,Lua总是会忽略所有长字符串开头的换行符,避免这个问题的简单方法就是在字符串起始处添加一个换行符。
以下quote函数就是根据这两个注意点编写的处理函数:
function quote(s) --
查找最长的等号序列
local n = -1
for w instring.gmatch(s, "]=*") do
n =math.max(n, #w - 1)
end
--
产生n+1个等号
local eq =string.rep("=", n+1)
--
生成长字符串的字面表示,在字符串开始的地方加了一个换行符
returnstring.format("[%s[\n%s]%s]", eq, s, eq)
end
12.2.1
保存无环的table
下一个任务是保存table。保存table的方法有几种,选用哪种方法取决于table的结构。简单的table
不仅需要简单的算法,而且更需要完美地输出结果。
第一个算法如下:
function serialize(o)
if type(o) =="number" then
io.write(o)
elseiftype(o) == "string" then
io.write(string.format("%q",o))
elseiftype(o) == "table" then
io.write("{\n")
for k, vin pairs(o) do
io.write("", k, " = ")
serialize(v)
io.write(",\n")
end
io.write("}\n")
else
error("cannotserialize a " .. type(o))
end
end
上面函数假设了一个table中的所有key都是合法的标示符。但如果一个table的key为数字或者为非法
的Lua标识符,那么就会出现问题。一个简单的解决方法是将这行代码:
io.write(" ", k, " = ")改为:
io.write(" ["); serialize(k);io.write("] = ")
这样便增强了这个函数的强健性,但却损失了结果文件的美观性。例如对于调用:
serialize{a=12, b='Lua', key='another"one"'}
第一个版本的serialize会输出:
{
a = 12,
b ="Lua",
key ="another \"one\"",
}
而第二个版本则输出:
{
["a"]= 12,
["b"]="Lua",
["key"]= "another \"one\"",
}
12.2.2
保存有环的table
如果要处理任意拓扑结构(带环的table或共享子table)的table,就需要采用另外一种方法。
保存函数要求将待保存的值及其名称一起作为参数传入;此外还必须持有一份所有已保持过的table的
名称记录,以此来检测环并复用其中的table。
代码如下:
function basicSerialize(o)
if type(o) =="number" then
return tostring(o)
else --
假设 o 是字符串
returnstring.format("%q", o)
end
end
functin save(name, value saved)
saved = savedor {}
--初始值
io.write(name,"=")
iftype(value) == "number" or type(value) == "string" then
io.write(basicSerialize(value),"\n")
elseiftype(value) == "table" then
if saved(value) then
-- 该value是否已经保存过?
io.write(saved[value], "\n")
-- 使用先前的名字
else
saved[value] = name
-- 为下次使用保持名字
io.write("{}\n")
-- 创建一个新的table
for k, v in pairs(value) do
-- 保存其字段
k = basicSerialize(k)
local fname = string.format("%s[%s]",name, k)
save(fname,v, saved)
end
end
else
error("cannotsave a ".. type(value))
end
end
示例——假设有一个table如下所示:
a = {x=1, y=2, {3,4,5}}
a[2] = a
-- 环
a.z = a[1]
-- 共享子table
调用save("a", a)
保存的结果为:
a = {}
a[1] = {}
a[1][1] = 3
a[1][2] = 4
a[1][3] = 5
a[2] = a
a["y"] = 2
a["x"] = 1
a["z"] = a[1]
如果想以共享的方式来保存几个table中的共同部分,只需调用save时使用相同的saved
参数。
相关文章推荐
- Lua 学习笔记(九) ——数据文件与持久性
- SAP BW平面文件特征数据的加载——视频学习笔记04和05
- R语言学习笔记(二)——从文件读取数据
- Lua学习笔记之数据类型
- Lua学习笔记8:文件读写
- 黑马程序员 C#学习笔记⑦ 文件系统数据一FileStream
- SAP BW_PA_文件_特征数据——视频学习笔记13
- 【北大天网搜索引擎TSE学习笔记】第11节——倒排索引等数据文件的建立(预处理子系统)
- 【hadoop】Hadoop学习笔记(七):使用distcp并行拷贝大数据文件
- 学习笔记之cocos2d-x2.1.1实现修改plist文件数据,用TinyXml解析XML
- Windows phone 8 学习笔记(2) 数据文件操作(转)
- SAP BW_PA_文件_事务数据——视频学习笔记15
- 孙鑫VC学习笔记:第十二讲 (五) 往win.ini的文件中写入数据
- Hibernate学习之 -- 使用Middlegen-Hibernate-r5创建oracle10g的table的hibernate映射文件,Hibernate学习笔记三 ---持久化类和关系数据
- Windows phone 8 学习笔记(2) 数据文件操作
- 学习笔记之cocos2d-x2.1.1实现修改plist文件数据,用TinyXml解析XML
- oracle 学习笔记 利用RMAN修复数据文件中的坏块
- oracle 11g 学习笔记 10_31_管理表空间和数据文件
- Lua学习笔记(一) —— 数据类型
- Android入门学习笔记(一)|基础知识|文件数据存储读取|解析XML