您的位置:首页 > 编程语言 > Lua

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
参数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  lua 脚本