table库进阶

table 库提供操作table对象的函数. table对象可以用来创建各种复杂的数据结构

一、字符串列表

什么是“字符串列表”:
字符串列表:以字符串形式表示table字典键值对的字符串对象。
其中每个元素是一个键值对,元素之间用分隔符分开。字符串列表是单层的不能嵌套。

例如:

str = "a=12,b=13,c=16";

这里的str就是一个字符串列表,分隔符为,号.

如:

str2 = [[
a=12
b=13
c=16
]]

str2 也是一个字符串列表,分隔符为换行符\n;

1、从table对象生成字符串列表。

str = table.saveList(tab,sep)
tab参数是一个table对象,sep参数是一个字符串对象指定分隔符。
这个函数是从tab生成相应的字符串列表。

要注意的是,这里的table对象必须是单层的,不能在元素中嵌套table子元素

2、从字符串列表生成table对象。

tab = table.loadList(str,sep);
str参数是一个字符串列表,sep参数是一个字符串对象指定分隔符。
str参数是一个字符串,返回值是一个table对象

str=[[
a=abc
b=1234
c=123456
d=1234567
]]
 
 
tab =table.loadList( str,"\n")
 
win.consoleOpen();
--遍历数组(索引为数字) 请注意下面使用的是无序的pairs而不是有序的ipairs
for i,v in pairs(tab) do
   print(i,v); --请注意:str的第一个空行被认为是一个数组元素。
end;

注意回车换行的问题:

注意

如果以二进制形式读入文本文本,
例如: str = string.load("c:\\strlist.txt","rb");
则文本的换行通常会是两个字符,即回车换行\r\n

而table.loadList是不能指定多个分隔符的。
我常常看到大家因为这个看不到的回车符而困惑。
比较字符串时发现不相等可是看上去好象是相等的。

这时候,我们可以用t模式读入文本,
例如: str = string.load("c:\\strlist.txt","rt");
t模式读入的文本通常是没有回车符号的。因此不会出一这样的问题。
当然,保存的时候也要指定同样的t模式以重新添加回车符。
例如: string.save( str,"c:\\strlist.txt"","w+t")

字符串列表虽然简单,但是在编程中非常有用,
我们也可以用来存取配置文件实现类似ini文件的功能、但是更简洁高效。

例如我们通常使用的存取ApeML自定义属性函数:
ape:.loadSetting() ape:.saveSetting() 其内部就是调用了table.saveList() table.loadList();

二、字符串数组

我经常看到大家用字符串列表来拆分普通的不包含键值对的字符串。

例如:

str = [[
12
1234
abcd16
]]

每行都不是键值对,不是字符串列表。用table.loadList来拆分这样的字符串不合适。
首先table.loadList得到的字符串列表是无序的,所有的元素没有顺序随机出现。
但对于上面这样的字符串,我们通常望得到的结果是有序的。

字符串数组:以字符串形式表示table数组的字符串对象,元素之间用分隔符分开。
字符串数组的元素不是键值对。字符串数组是有序操作的。


1、从table对象生成字符串数组。

str = table.concat (tab [, sep [, i [, j]]])
tab参数是一个table对象,sep参数是一个字符串对象指定分隔符。sep参数是一个可选参数。
第三个可选参数指定起始索引,第四个可选参数指定结束索引。
这个函数是从tab生成相应的字符串数组。

更多关于这个函数的用法请参考:数据结构-字符串缓冲

要注意的是,这里的table对象必须是单层的,不能在元素中嵌套table子元素

2、从字符串数组生成table对象。

tab = string.split( str ,sep)
str参数是一个字符串列表,sep参数是一个字符串对象指定分隔符(可指定多个,可使用模式匹配),返
回值是一个table数组。

str=[[
abc
1234
123456
1234567
]]
 
 
tab = string.split( str,"\n")
 
win.consoleOpen();
--遍历数组(索引为数字) 请注意下面使用的是有序的ipairs而不是无序的pairs
for i,v in ipairs(tab) do
   print(i,v);
end;
注意

如果以二进制形式读入文本文本,
例如: str = string.load("c:\\strlist.txt","rb");
则文本的换行通常会是两个字符,即回车换行\r\n

而string.split是可以指定多个分隔符的。这样写就行了:
tab = string.split( str,"\r\n")

string.split会自动的合并连续的分隔符,例如下面代码tab有两个元素,而不是5个:
str="a,,,,,b"
tab = string.split( str,","); --tab获取两个元素
有时候我们不需要自动合并分隔符的功能。这时候可以用string.splitX函数。
str="a,,,,,b"
tab = string.splitX( str,",");

win.messageBox(table.maxn(tab)); -->显示tab有五个元素

三、XML序列化table对象

以xml格式文档描述、存取table对象。xml的结构必须符合table的描述规则。只有约定格式的xml才能描述table。

ok = table.save(tab,xmlfile)
序列化table对象到xml文件,第二个参数xmlfile指定文件名。
成功返回true,失败返回nil;
示例如下:

tab ={a="123",b="333",456,{123,a="ddddddd"}};
 
win.consoleOpen();
print( table.save(tab,"c:\\test.xml") )

xmlstr = table.save(tab)
序列化table对象,如果没有第二个参数(文件路径),那么将返回一个XML字符串
示例如下:

tab ={a="123",b="333",456,{123,a="ddddddd"}};
win.messageBox( table.save(tab) );

tab = table.load(xmlfile)
从xml文件读取xml对象,xmlfile参数指定文件名。
示例如下:

-- 一个小技巧,用win.lastError来检查找开文件时遇到的错误
 
tab = assert( table.load("c:\\nofile.xml"),select(2,win.lastError() ) );


tab = table.load(xmlstr)
从xml格式的字符串生成table对象。

下面是完整的示例程序:

tab = {a=123;name="value"}
xmlstr = table.save(tab);--把tab序列化到xml格式的字符串
 
win.messageBox(xmlstr,[[tab = {a=123;name="value"}]])
 
tab = table.load(xmlstr );--从xml格式的字符串生成tab
xmlstr2 = table.save(tab);--把tab序列化到xml格式的字符串

if(xmlstr2==xmlstr)then
    win.messageBox( "多次转换的结果准确一致");
end;

在fap程序中序列化到ApeML的数据区块
我们可以用fap程序提供的函数序列化table对象到数据区块
适用于将较小的table对象直接保存到ApeML的“数据区块”节点中。

tab = ape:loadTable("table_key");
从数据区块读取table对象,成功返回table对象,失败返回nil;
"table_key"在数据区块中标识这个table对象,以避免与其他的table对象混淆。

tab = assert(ape:loadTable("table_key"),"数据区块中没有table_key");

ape:saveTable(tab,"table_key")

将一个table对象保存到ApeML的数据区块,
其中table_key参数可以为空或省略(默认为"table")。

注意
xml是文本文件,如果table的成员包含二进制的数据,应当法将字符串进行base64编码然后再存储到xml中。
例如: str = string.encode(str); 从XML文档还原对象时,用 str = string.decode(str)进行base64解码。

四、读写XML文档

以table对象描述、存取xml格式文档。 table的结构必须符合xml读写规则。只有约定格式的table才能描述xml。

注意
xml读写xml序列化是有区别的,

xml序列化是用来描述table对象,所以XML文档要遵循LAScript约定的格式以准确描述table的成员。
xml序列化:以xml格式文档描述、存取table对象。xml的结构必须符合table的描述规则。只有约定格式的xml才能描述table。

读写XML文件适用任何符合XML标准的文档。
但是所有读写的数据都会被自动转换为字符串格式(string)。不会在XML中额外增加描述数据类型的属性。
xml读写:以table对象描述、存取xml格式文档。 table的结构必须符合xml读写规则。只有约定格式的table才能描述xml。


tab = table.loadXML(str)
str参数可是是文件路径、XML数据(string 字符串);
成功返回table对象,失败返回nil;

ok = table.saveXML(tab,filepath)
xmlstr = table.saveXML(tab)

指定filepath则保存XML到文件,成功返回true,失败返回false
不指定filepath则返回XML数据(string 字符串)

XML转换为table以后,属性转换为table的元素(属性名转换为键)
子节点转换为嵌套的table,节点名称将转换为 _xml_tag 键。
从table对象转换为XML数据,tab["_xml_tag"]将被自动转换为节点名称。

_xml_tag _xml_value
通过table.loadXML(str)生成的table会有两个特定的属性_xml_tag _xml_value

_xml_tag的值可能为以下几种:
1、"_xml_cdata"
2、"_xml_comment"
3、"_xml_text"
4、普通节点名称

如果是普通节点,则_xml_value 值为nil
否则 _xml_value 中记录相应节点的数据

附:用MSXML操作XML文件的一个例子

--下面的代码用table对象生成一个简单的xml文件
tab ={a=123,b="这是一个文本"};
table.save(tab,"c:\\test.xml");
 
--导入comx插件,如果没有就下载
import("std");--这句主要是导入可自动下载插件的import2函数
import2("comx","http://www.yhhe.net/ape/import/comx/comx.dll");
 
--创建com对象
rootXML = comx.CreateObject("MSXML.DOMDocument") ;
--载入xml文件
rootXML:load("c:\\test.xml") ;
 
--获取根节点
root = rootXML.documentelement;
 
--root.childNodes:item(1)表示第一个子节点
--text = root.childNodes:item(2).attributes:item(1).value
--因为item是默认方法,可以省略简写如下
text = root.childNodes(1).attributes(1).value
win.messageBox(text);-->显示"这是一个文本"
 
--释放com对象
rootXML = nil;
root = nil;
collectgarbage();