|
2#
楼主 |
发表于 2007-7-9 23:36:47
|
只看该作者
TCL脚本数据文件格式 -by Koen Van Damme
简介
一个典型的tcl脚本把它的内部数据保存在列表和数组(tcl中两种主要的数据结构)中.比如,假定你想写一个能将数据先保存在磁盘上,然后再读取的tcl应用程序, 这将使你的用户可以先把一个项目保存下来,以后再重新装入.你需要一个办法,把数据从其内部存储处(列表与数组)写入到一个文件中,同样,也要有一个办法把数据从文件中读出装入到正在运行的脚本中去.
你可以选择把数据保存为二进制格式或文本格式.本文讨论的仅限文本格式,我们将考虑几种可能的数据格式及如何用tcl来进行分析.我们会特别介绍一些简单的技巧,使文本文件分析更容易.
本文假定你对tcl语言很熟悉,至少已经用tcl语言写过几个脚本.
▲一个简单的例子
假定你有一个简单的绘图工具,能把文本和长方形放到画布上.为了保存画好的图,你需要一个必须容易读取的文本格式的文件,最先想到而且最容易的文件是这样的:
example1/datafile.dat
rectangle 10 10 150 50 2 blue
rectangle 7 7 153 53 2 blue
text 80 30 "Simple Drawing Tool" c red
The first two lines of this file represent the data for two blue, horizontally stretched rectangles with a line thickness of 3. The final line places a piece of red text, anchored at the center (hence the "c"), in the middle of the two rectangles.
文件的前两行代表两个蓝色的水平展开的长方形,线条宽度是2(原文此处为3,可能是笔误,译者注).最后一行放了一段红色的文字,定位在中心(由"c"来指定)----在两个长方形的中间.
用文本文件保存你的数据使程序的调试更容易,因为你可以检查程序输出来保证一切都正常。同时也允许用户手工修改保存的数据(这样做可能好,也可能不好,取决于你的意图).
当你读取这种格式的文件时,或许得先对文件进行分析然后据此创建数据结构.分析文件时,你要一行一行地尝试,使用象regexp这类的工具来分析文本不同的部分.下面是一个可能的过程:
example1/parser.tcl
canvas .c
pack .c
set fid [open "datafile.dat" r]
while { ![eof $fid] } {
# Read a line from the file and analyse it.
gets $fid line
if { [regexp \
{^rectangle +([0-9]+) +([0-9]+) +([0-9]+) +([0-9]+) +([0-9]+) +(.*)$} \
$line dummy x1 y1 x2 y2 thickness color] } {
.c create rectangle $x1 $y1 $x2 $y2 -width $thickness -outline $color
} elseif { [regexp \
{^text +([0-9]+) +([0-9]+) +("[^"]*") +([^ ]+) +(.*)$} \
$line dummy x y txt anchor color] } {
.c create text $x $y -text $txt -anchor $anchor -fill $color
} elseif { [regexp {^ *$} $line] } {
# Ignore blank lines
} else {
puts "error: unknown keyword."
}
}
close $fid
我们一次读取一行数据,使用正则表达式查找该行代表的是某种数据类型.通过检查第一个词,我们可以区分代表长方形的数据和代表文本的数据,所以第一个词是一个关键字,它明确地告诉我们正在处理的是什么类型的数据.同样我们分析每个项目的坐标,颜色和其他属性.括号中正则表达式的分组部分使我们找到变量'x1','x2'等的分析后的结果.
假如你知道正则表达式如何工作,这看上去是一个很简单的实现.但我觉得它有点难以维护,正则表达式也使其难以理解.
还有一个更简捷的解决方法,叫做“active file(主动文件)”.原本由Nat Pryce在设计样本时想到的。这种方法基于一个非常简单的提议:与其用TCL自己来写语法分析器(用regexp或其他途径),干嘛不让TCL的语法分析器为你做这些工作呢?
▲主动文件设计样本
为解释这种设计样本,我们继续使用上节中那个简单的绘图工具。首先我们用TCL语言写两个过程,一个画矩形,一个写文本。
example2/parser.tcl
canvas .c
pack .c
proc d_rect {x1 y1 x2 y2 thickness color} {
.c create rectangle $x1 $y1 $x2 $y2 -width $thickness -outline $color
}
proc d_text {x y text anchor color} {
.c create text $x $y -text $text -anchor $anchor -fill $color
}
现在要在画布上绘图,我们调用这两个过程就行了,每次调用其中的一项。比如要画如前所述的图形,需要下面三个调用。
example2/datafile.dat
d_rect 10 10 150 50 2 blue
d_rect 7 7 153 53 2 blue
d_text 80 30 "Simple Drawing Tool" c red
看上去眼熟吗?调用过程的代码看上去与先前我们分析的代码几乎完全一样。唯一的不同之处是关键词由"rectangle"和"text"变成了"d_rect"和"d_text".
现在我们看到了写样本的技巧:为分析数据文件,我们要把它当作一个TCL脚本来对待。我们只把对我们写好的过程的调用放到一个文件中,并用此文件作为数据文件.设计样本的核心是数据文件实际上包含着对TCL过程的调用.
分析数据文件现在太容易了:
source "datafile.dat"
内建的TCL命令source读取文件,分析并执行文件中的命令.因为我们已经完成了d_rect和d_text过程,source命令将自动以正确的参数调用这两个过程.我们将d_rect和d_text称为分析过程.
我们无需再做任何分析,不用正则表达式,不用一行一行地循环,不用打开/关闭文件.只需调用source命令就完成了所有的工作。
数据文件已经成了可以执行的TCL脚本.因为它包含的是可执行命令,而不仅仅是被动的数据,所以称之为主动文件.主动文件在大多数脚本语言环境中均可正常运行,在Nat Pryce的主页上对其有详细的描述.
▲使用主动文件样本的优点:
无需再写一个分析程序,source调用TCL分析程序即可完成.
容易读取数据文件格式.
使用主动文件样本的缺点:
如果数据文件包含有危险命令,象l -a exec rm *,它们执行后会带来严重的后果.解决这个问题的办法是在安全模式下执行主动文件,防止危险命令。具体信息可参看TCL手册中"安全解释器"部分.
▲主动文件样本的局限
此样本不是对所有可能的数据格式都有效.数据格式必须是以行为基础的,每一行必须以一个关键字开头.用关键字开头写TCL过程,就把被动的关键字变成了主动的命令。这也意味着你不能使用象if或while之类的关键字,因为TCL不允许你用这样的名字来写过程.事实上,上面的例子中我把关键字改为d_text,就是因为开发工具包已经有了保留字text,该命令用来创建文本工具.
▲英语言过程
至此我们已经可以写一个简单的文件格式了:
d_rect 10 10 150 50 2 blue
d_rect 7 7 153 53 2 blue
d_text 80 30 "Simple Drawing Tool" c red
我们还有一个很简单的分析程序,就是两个分析过程和source命令.现在,我们看一下如何来进一步改进.
当你观察大量此类数据时,极易被数据搞糊涂.第一行包含10 10 110 50 3,你得有些这方面的经验才能很快明白前两个代表一个坐标,后两个是另一个坐标,最后一个是线宽.我们能用在数据中引入附加文本的方法来使一个程序员在阅读时较为容易.
example3/datafile.dat
d_rect from 10 10 to 150 50 thick 2 clr blue
d_rect from 7 7 to 153 53 thick 2 clr blue
d_text at 80 30 "Simple Drawing Tool" anchor c clr red
介词to和from,参数名thick和color使数据看上去更象英语句子了,为适应这些介词,我们的分析过程需要其他的附加参数:
example3/parser.tcl
proc d_rect {from x1 y1 to x2 y2 thick thickness clr color} {
.c create rectangle $x1 $y1 $x2 $y2 -width $thickness -outline $color
}
正如你所看到的,执行过程并未改变.新参数在过程体中并未使用;其目的仅仅是为了使用数据可读性更强.
▲选项/数值对
Tk工具包提供了一个创建图形界面部件的集合.这些部件以选项和他们的值来加以配置,配置的语法很简单(一个横线,后跟选项名,再后面是其值)而且标准化(许多其他的TCL扩展集使用相同的语法来配置其部件).
使用选项/数值对后,数据文件看上去象这样:
example4/datafile.dat
d_rect -x1 10 -y1 10 -x2 150 -y2 50 -thickness 2
d_rect -thickness 2 -x1 7 -y1 7 -x2 153 -y2 53
d_text -x 80 -y 30 -text "Simple Drawing Tool" -anchor c -color red
为分析数据,我们需要在分析过程d_rect和d_text中引入选项/数值对,我们首先试一下使用与英语过程相似的哑变量.
proc d_rect {opt1 x1 opt2 y1 opt3 x2 opt4 y2 opt5 thickness opt6 color} {
.c create rectangle $x1 $y1 $x2 $y2 -width $thickness -outline $color
}
我们再一次看到,实现的过程并未改变.尽管这个解决方案只对最简单的数据格式有效,但它很清晰明了.它的优点有两个:选项在参数列表中的位置是固定的.比如,你不能把color(颜色属性)放在thickness(线宽属性)前面.对一个纯数据文件格式来说这个方法还不错(因为数值往往按相同的顺序存储),但当你想将其用于脚本中的手工输入数据时,这个方法则成了一个障碍.
选项没有默认值:你必须提供所有选项的值,而不能遗漏其中任何一个.
下面是一个可解决所有问题的实现过程.
example4/parser.tcl
proc d_rect {args} {
# First, specify some defaults
set a(-thickness) 1
set a(-color) blue
# Then, 'parse' the user-supplied options and values
array set a $args
# Create the rectangle
.c create rectangle $a(-x1) $a(-y1) $a(-x2) $a(-y2) \
-width $a(-thickness) -outline $a(-color)
}
与使用一个长长的参数表不同,分析过程现在仅有一个名为args的参数,由它来收集调用过程时所有的实际参数.参数x1,y1等消失了.他们现在由一个局部的数组来处理,稍后我们将圆心解释.
代码的第一部分为选项设定默认值,第二部分分析args中的选项/数值对.TCL内建的数组处理模块对此做得非常得心映手.它先在数组a中创建新的入口,使用选项名(包括前导横线"-")作为索引,选项值作为数组值.
如果用户在调用中不指定-color选项,a(-color)的入口默认值保持不变. 除用数组入口代替过程参数外,过程体中的最后一行与前面的实现一样.
如果用户调用时忘记指定选项-x1,则-x1的数组入口不会被设置(没有其默认值),创建矩形的调用就会引发一个错误.此例说明你可以给其中一些选项指定默认值,使其可随意选择,而另一些则不指定默认值,强制其必须由用户指定.
▲最好的格式通常是各种方法的结合
现在我们已经明白了TCL数据文件的常见方法(主动文件,英语言过程,选项/数值对),我们可以将其各自的优点组合进一个单独的数据格式中去.对强制性选项,我们使用固定位置参数时,多半与哑介词相结合增强可读性(见英语言过程).而所有的可随意选择的选项,宜用选项/数值对机制来进行处理,好让用户可以空着选项或在调用时改变其位置.最后,数据文件可能会是这样的:
d_rect from 10 10 to 150 50 -thickness 2
d_rect from 7 7 to 153 53 -thickness 2
d_text at 60 30 "Simple Drawing Tool" -anchor c -color red
假定所有项目的color属性的默认值都是"blue".
作为一个个人习惯,我通常会写这样的命令:
d_rect \
from 10 10 \
to 150 50 \
-thickness 2
d_rect \
from 7 7 \
to 153 53 \
-thickness 2
d_text \
at 80 30 "Simple Drawing Tool" \
-anchor c \
-color red
I find it slightly more readable, but that's all a matter of personal taste (or in my case lack of taste :-).
我觉得可读性要好一些,但这仅是一个个人偏好的问题.(or in my case lack of taste)(这句话是作者在调侃自己,但我不知如何把它译出来,请哪位大侠帮忙指点一下,译者注)
-------------------------------------------------------------------------------- |
|