写在前面的话 在论坛上看到这篇文章时,一时冲动,发出一个贴子:Let me try.等真正大致看了一下原文后,才有些后悔,倒不是怕工作量太大,只是担心以自己的英文水平能否把这个文章按照作者的意思表达清楚.不怕各位笑话,在此之前,我对TCL几乎是没有听说过(只知道TCL----王牌),更不用说掌握了.没有办法,只能现学现卖,在网上找到相关的介绍TCL的文章,认真地对其进行了了解,也只能是了解. 说点题外话,我现在对计算机这一行,真的有些倦了,因为作为一个系统管理员,每天都有更新的东西在等待你去学习,必须不停地学,而不能有片刻的停顿,否则你就将面临被淘汰的危险,有时真的感觉很累,但没有办法,这就是生活,头天晚上你对着厚厚的书本说,我看到你就恶心,再也不想碰你了.但睡一觉醒来,你还是得把它当作一位良师,一位益友. 我的英文水平一般,况且TCL语言对我是个新事物,尽管现在有了一定的了解,但其中的一些术语,我还是理解的不够透彻.所有译文中的有些地方可能让大家觉得有些迷惑,甚至可能有些可笑,对此还请各位谅解.也希望有高手能指出译文中的错误,别让我的劣作影响了各位网友的学习. 同时也真诚希望大家给我来信,交朋友,共同提高. 我的email:zkzxl@etang.com 不知我的译文出来前,是否已经有网友为各位译出来,希望能够互相交流一下. ********************************************************************************
TCL脚本数据文件格式 简介 一个典型的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 |