Programming in Lua, 2Nd Edition - Chapter 12: Data Files and Persistence

本文探讨了使用Lua处理数据文件的方法,包括自定义数据文件格式、自描述数据格式及序列化技术。介绍了如何利用Lua构造器进行数据记录,以及如何处理带有循环引用的表。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 

 

 

Chapter 12: Data Files and Persistence

 

写文件比读文件要容易。写文件我们可以获得完全的控制,而读文件我们无法知道会有什么意外发生。

 

12.1 Data Files

 

如果数据文件是预定义格式,如CSVXML,我们选择很少。但是,如果我们想要创建我们的自定义文件,我们可以用Lua constructors 作为文件格式。这种格式将每条数据记录表示成Lua 构造器。

 

作为这种数据文件的替代:

 

Donald E. Knuth,Literate Programming,CSLI,1992

Jon Bentley,More Programming Pearls,Addison-Wesley,1990

 

我们写:

-- 文件data 的内容

Entry{"Donald E. Knuth",

"Literate Programming",

"CSLI",

1992}

Entry{"Jon Bentley",

"More Programming Pearls",

"Addison-Wesley",

1990}

 

local count = 0

function Entry (_) count = count + 1 end

dofile("data")

print("number of entries: " .. count)

 

这段代码计算有多少条EntryEntry{code} 等同于Entry({code}) ,是一个函数调用,table 将为它的参数。所以,数据变成了lua 程序的一部分

 


 

自描述数据格式

-- 文件data

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 = {} -- 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 存进一个表。当某些条目没有author 字段时,我们可以对代码稍作改动:

 

function Entry (b)

       if b.author then authors[b.author] = true end

end

 

Lua 不只运行快,而且编译快。例如,上面的列出2 M数据所有的authors 不超过一秒。 这不是运气好。数据描述已经成为Lua 中的一种重要应用。

 


 

12.2 Serialization

 

有时我们需要序列化一些数据,就是将数据转换成字节流或字符流,这样我们得以将它存入文件或是通过网络发送出去。

 

function serialize (o)

       if type(o) == "number" then

              io.write(o)

       elseif type(o) == "string" then

              io.write(string.format("%q", o))

       else <other cases>

       end

end

 

这一句string.format("%q", a)) 是出于安全的原因,它将任意串包含进两个类似“[[ ]]”的符号里面。避免io.write("[[", o, "]]") 展开成varname = [[ ]]..os.execute('rm *')..[[ ]] 这种危险的形式。

 

Lua 5.1 提供另一种方法“[=[...]=] 来处理长串,但是这是一种新符号,主要意图是为手写代码准备的。

 

”[==[]==]”的形式包围任意串的函数

 

function quote (s)

       -- find maximum length of sequences of equal signs

       local n = -1

       for w in string.gmatch(s, "]=*") do

              n = math.max(n, #w - 1)

       end

       -- produce a string with 'n' plus one equal signs

       local eq = string.rep("=", n + 1)

       -- build quoted string

       return string.format(" [%s[/n%s]%s] ", eq, s, eq)

end

 

s = [===[

       [=["aa"dfd[[]]]----]=]

       [==[dsaffdsewfdsjk]==]

]===]

 

ss = quote(s)

print(ss)

 

 

Saving tables without cycles

 

我们的下一个任务(更因难了)是保存表。椐据不同表的限制,有许多不同的方法。并没有单个算法可以应付所有情况。

 

如果表的数字索引或串索引不是Lua 有效的标识符,我们就有麻烦了。

 

function serialize (o)

       if type(o) == "number" then

              io.write(o)

       elseif type(o) == "string" then

              io.write(string.format("%q", o))

       elseif type(o) == "table" then

              io.write("{/n")

              for k,v in pairs(o) do

                     io.write(" ", k, " = ")

                     serialize(v)

                     io.write(",/n")

              end

              io.write("}/n")

       else

              error("cannot serialize a " .. type(o))

       end

end

 

serialize{a=12, b='Lua', key='another "one"'}

 

 

Saving tables with cycles

 

function basicSerialize (o)

       if type(o) == "number" then

              return tostring(o)

       else -- assume it is a string

              return string.format("%q", o)

       end

end

 

function save (name, value, saved)

       saved = saved or {} -- initial value

       io.write(name, " = ")

       if type(value) == "number" or type(value) == "string" then

              io.write(basicSerialize(value), "/n")

       elseif type(value) == "table" then

              if saved[value] then -- value already saved?

                     io.write(saved[value], "/n") -- use its previous name

              else

                     saved[value] = name -- save name for next time

                     io.write("{}/n") -- create a new table

                     for k,v in pairs(value) do -- save its fields

                            k = basicSerialize(k)

                            local fname = string.format("%s[%s]", name, k)

                            save(fname, v, saved)

                     end

              end

       else

              error("cannot save a " .. type(value))

       end

end

 

a = {x=1, y=2; {3,4,5}}

a[2] = a -- cycle

a.z = a[1] -- shared subtable

 

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]

----------------------

 


 

a = {{"one", "two"}, 3}

b = {k = a[1]}

 

save("a", a)

save("b", b)

 

输出结果为:

---------------------

a = {}

a[1] = {}

a[1][1] = "one"

a[1][2] = "two"

a[2] = 3

b = {}

b["k"] = {}

b["k"][1] = "one"

b["k"][2] = "two"

---------------------

 

a = {{"one", "two"}, 3}

b = {k = a[1]}

local t = {}

save("a", a, t)

save("b", b, t)

 

输出结果为:

-------------------

a = {}

a[1] = {}

a[1][1] = "one"

a[1][2] = "two"

a[2] = 3

b = {}

b["k"] = a[1]

-------------------

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值