快速掌握Lua 5.3 —— 数据操作
2016-01-27 12:16
323 查看
Q:如何利用Lua构造器操作数据?
A:将每条数据作为Lua中的一个”table”构造,将数据中的每一个域作为”table”中的一个”value”。比如下面这些数据,Donald E. Knuth,Literate Programming,CSLI,1992
Jon Bentley,More Programming Pearls,Addison-Wesley,1990
在Lua中使用”table”构造可以表示为,
[code]-- "data.lua"文件中内容。 Entry{"Donald E. Knuth", "Literate Programming", "CSLI", 1992} Entry{"Jon Bentley", "More Programming Pearls", "Addison-Wesley", 1990} -- 因为"Entry{}"与"Entry({})"相同,所以之后在Lua中可以使用回调函数的方式使用这些数据,函数的参数就是数据本身。 -- 计算数据中有几条记录。 local count = 0 function Entry (b) count = count + 1 end dofile("data.lua") -- 引入文件内容并执行。 print("number of entries: " .. count) --> number of entries: 2 -- 存储文件中所有作者名(Entry的第一个域),并打印他们。 local authors = {} -- a set to collect authors function Entry (b) authors[b[1]] = true end dofile("data.lua") -- 引入文件内容并执行。 for name in pairs(authors) do print(name) end
但是上面的最后一个例子存在潜在的隐患。如果有的数据没有”author”域,那么
b[1]取到的就不一定是什么值了。
[code]-- 所以如果对数据大小不太关心的话,可以使用"self-describing data format"的方式存储数据,每个变量值的含义都有一个名称来描述。 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 } -- 那么上面的最后一个例子可以更改为: local authors = {} -- a set to collect authors function Entry (b) --[[ 有了"self-describing data format",这里就可以判断记录中是否有该域了。 同时"author"写在数据中的哪个域都没关系(不一定必须写在第一个域了)。]] if b.author then authors[b.author] = true end end dofile("data.lua") -- 引入文件内容并执行。 for name in pairs(authors) do print(name) end
Q:什么是数据的序列化?
A:我们经常需要序列化一些数据,为了将数据转换为字节流或者字符流,这样我们就可以保存到文件或者通过网络发送出去。
[code]-- 对数字的序列化。 if type(o) == "number" then io.write(o) end -- 对字符串的序列化。 if type(o) == "number" then io.write(string.format("%q", o)) -- "%q"可以安全的处理字符串。详见"附加1"。 end
Q:如何将数据序列化为”expressions”?
A:[code]--[[ o: 待序列化的数据。 pre_type: 待序列化的数据被包含在哪种类型的数据中。 indent: 缩进的个数。]] function serialize (o, pre_type, indent) indent = indent or 0 -- 缩进的个数。默认是0,即不缩进。 if type(o) == "number" then -- 对数字的序列化。 io.write(string.format("%d", o)) -- 如果数据是在"table"中,那么不能换行,因为之后有数据要打印。 if("table" ~= pre_type) then io.write("\n") end elseif type(o) == "string" then -- 对字符串的序列化。 io.write(string.format("%q", o)) -- 如果数据是在"table"中,那么不能换行,因为之后有数据要打印。 if("table" ~= pre_type) then io.write("\n") end elseif type(o) == "table" then -- 对"table"的序列化。 io.write("{\n") for k,v in pairs(o) do for i = indent, 0, -1 do io.write("\t") end io.write("[") serialize(k, "table", indent + 1) -- 对"key"继续进行序列化。 io.write("] = ") serialize(v, "table", indent + 1) -- 对"value"继续进行序列化。 io.write(",\n") end -- "}"需要少打印一个缩进,否则与其中的数据一样的缩进了。 for i = indent - 1, 0, -1 do io.write("\t") end io.write("}") else error("cannot serialize a " .. type(o)) end end -- data number = 5 string = "abcdefg" table = { 5, 'one', 'another "one"', [10] = 9, first_str_index = 23, ["second_str_index"] = 67, { 1, 2, { x = 12, y = 56 }, 3 } } -- test serialize(number) serialize(string) serialize(table) io.write("\n") -- result 5 "abcdefg" { [1] = 5, [2] = "one", [3] = "another \"one\"", [4] = { [1] = 1, [2] = 2, [3] = { ["x"] = 12, ["y"] = 56, }, [4] = 3, }, ["first_str_index"] = 23, [10] = 9, ["second_str_index"] = 67, }
接收到被序列化为”expressions”数据的程序可以使用变量存储数据。
Q:如何将数据序列化为”statements”?
A:[code]--[[ 限制被序列化的数据只能是"number"或者"string"。 (对"table"的序列化,主函数中有特殊处理。)]] function basicSerialize (o) if type(o) == "number" then return tostring(o) else -- assume it is a string return string.format("%q", o) end end --[[ name: 被序列化的数据的变量名。 value: 被序列化的数据的变量值。 saved: 存储已经被序列化的数据的表。]] function save (name, value, saved) saved = saved or {} -- 存储已经被序列化的数据。 io.write(name, " = ") -- 打印变量名。 -- 如果数据是"number"或者"string",则直接打印变量值。 if type(value) == "number" or type(value) == "string" then io.write(basicSerialize(value), "\n") -- 如果数据是"table"。 elseif type(value) == "table" then if saved[value] then -- 如果该数据被序列化过,则不重复序列化,直接打印该数据的名字。 io.write(saved[value], "\n") else -- 如果该数据未被序列化过。 saved[value] = name -- 在saved中存储该数据值与名字的映射。 io.write("{}\n") -- create a new table -- 依次序列化"table"中的每一个域。 for k,v in pairs(value) do local fieldname = string.format("%s[%s]", name, basicSerialize(k)) save(fieldname, v, saved) end end else error("cannot save a " .. type(value)) end end -- data number = 5 string = "abcdefg" table_2 = { x = 12, y = 56 } table_1 = { 1, 2, table_2, 3 } table = { 5, 'one', 'another "one"', [10] = 9, first_str_index = 23, ["second_str_index"] = 67, table_1, table_2 -- "table_2"在上面的"table_1"中已经被序列化过,不会被重复序列化。 } -- test save("var_n", number) save("var_s", string) save("var_t", table) -- result var_n = 5 var_s = "abcdefg" -- ↓ 这一部分将"table_1"序列化。 var_t = {} var_t[1] = 5 var_t[2] = "one" var_t[3] = "another \"one\"" var_t[4] = {} var_t[4][1] = 1 var_t[4][2] = 2 -- ↓ 这一部分将"table_2"序列化。 var_t[4][3] = {} var_t[4][3]["x"] = 12 var_t[4][3]["y"] = 56 -- ↑ 这一部分将"table_2"序列化。 var_t[4][4] = 3 var_t[5] = var_t[4][3] <-- "table_2"已经被序列化过,不会被重复序列化,只是打印被序列化时的名字。 var_t["first_str_index"] = 23 var_t[10] = 9 var_t["second_str_index"] = 67 -- ↑ 这一部分将"table_1"序列化。
接收到被序列化为”statements”数据的程序可以将数据直接存储在Lua脚本中,之后直接执行脚本使用。
附加:
1、对字符串的序列化最开始的想法可能是,[code]if type(o) == "string" then io.write("'", o, "'") end
但是这种实现方式对于一些特殊的字符(比如单引号,换行符等)无效。所以实现方式可能演变为,
[code]-- WARNING: bad code ahead!! if type(o) == "string" then io.write("[[", o, "]]") end
千万不要如此实现!!!
双引号是针对手写的字符串的而不是针对自动产生的字符串。如果有人恶意的引导你的程序去使用
" ]]..os.execute('rm *')..[[ "这样的方式序列化,你最终的”chunk”将是这个样子,
[code]varname = [[ ]]..os.execute('rm *')..[[ ]]
执行这句代码的后果可想而知。
为了以安全的方式引用任意的字符串,”string”标准库提供了格式化函数专门提供
"%q"选项。它可以使用双引号表示字符串并且可以正确的处理包含引号和换行等特殊字符的字符串。
2、对于将数据序列化为”statements”的例子,你可以控制共享的数据是否需要被重复序列化。
共享的数据会被重复序列化,
[code]-- data a = {{"one", "two"}, 3} b = {k = a[1]} -- test -- 未指定"save()"的第三个参数,所以每次调用"save()",其中的"saved"都是一个新的"table"。 save('a', a) save('b', b) -- result a = {} -- ↓ 这一部分将"a[1]"序列化。 a[1] = {} a[1][1] = "one" a[1][2] = "two" -- ↑ 这一部分将"a[1]"序列化。 a[2] = 3 b = {} -- ↓ 这一部分将"a[1]"重复序列化,只是换了个名字。 b["k"] = {} b["k"][1] = "one" b["k"][2] = "two" -- ↑ 这一部分将"a[1]"重复序列化,只是换了个名字。
共享的数据不会被重复序列化,
[code]-- test local t = {} -- 制定了"save()"的第三个参数,所以每次调用"save()",其中的"saved"都是相同的"table"。 save('a', a, t) save('b', b, t) -- result a = {} -- ↓ 这一部分将"a[1]"序列化。 a[1] = {} a[1][1] = "one" a[1][2] = "two" -- ↑ 这一部分将"a[1]"序列化。 a[2] = 3 b = {} b["k"] = a[1] <-- "a[1]"未被重复序列化。
相关文章推荐
- lua 编译、执行和错误
- Lua学习笔记
- Lua 迭代器
- Lua按指定字符分隔字符串的3种方法
- 初识 love2d. Hello World & lua 调试 & iOS 环境运行
- 使用lua代码创建文件夹,解压zip文件到指定目录(亲测可用)
- lua io函数
- lua os函数
- lua table函数
- lua重要函数
- Lua string函数说明
- lua 字符串模式匹配
- 为什么 Lua 的新版本越来越慢?
- LUAMD5加密
- LUAMD5加密
- Lua学习笔记(函数)
- lua与游戏测试(二)
- lua 与游戏测试 (一)
- lua的table库中经常使用的函数
- lua之m进制转换为n进制-任意进制转换算法