使用TCL脚本读取配置文件

引用地址:http://www.rjzl.gov.cn/news.asp?id=814

 

摘 要:unix下使用TCL脚本读取配置文件;错误处理.


关键词:TCL、配置文件、unix

一.应用范围
在实际工作中,TCL脚本对于一些简单的工作裨益甚大。通常编写脚本都有一定的模
式,首先从配置文件读入配置项,初始化变量,然后进行处理,最后输出,在程序
的运行过程中需要把一些信息写入日志文件,而调试信息写入日志文件和直接输出
到屏幕都可以。
使用任何一种脚本,通常也都会根据实际情况建立起自己的函数库,而这个函数库
中对配置文件配置项的读取无疑是非常基本而重要的。
这篇文章的本意是引导刚接触TCL脚本的朋友尽快上手,所以有些细节的说明文字比
较细。

二.程序讲解
构建一个配置文件,尽可能的接近实际应用:
文件名:config.ini
文件内容:
       #
# 这是注释行
#
[]
key1=value1         # 注释
[section1]
key2=value2
[section2]
key1=value2
[section1]
key1=xxxxxx        # 这是最后的返回
[section1]
key1=value2
现在开始对程序的说明:
;#-------------------------------------------------------------------------
;# 功能:从配置文件中读取配置项
;# 输入:1. configFile :配置文件名称
;#       2. Section : 段名称
;#       3. Key : 关键字
;#       4. Comment : 注释符,缺省为井号
;#       5. Equal : 关键字和值的分隔符,缺省为等号
;# 输出:1. Value : 相应的值
;#-------------------------------------------------------------------------
1. 过程的定义:
   格式:proc name args body
  要点:

         参数列表使用花括号引起;

          变量没有类型;

          变量之间使用空格间隔;

          如果参数有缺省值,使用花括号引起,并赋值

proc getConfig { configFile Section Key {Comment "#"} {Equal "="}} {

       set Value ""                   ;# 记录过程返回的值  
       set FindSection 0              ;# 记录是否找到了section
2.错误的处理
       格式:catch script ?varName?
       功能:执行script,如果成功返回TCL_OK(0),否则返回TCL_ERROR(1),提
示结果存在varName中。比如下面如果打开文件成功,errMsg返回的就是类似file3这
样的字符串,此时err返回的是TCL_OK,就是零;如果文件不存在,errMsg返回的类
似:couldn't open "config1.ini": no such file or directory,此时err返回TCL_ERROR,
就是一。
3.文件的读写
      格式:openfile fileName ? access ? permission
       讲解:
          fileName是文件名称;
          access是存取模式,可以为r,  r+, w, w+, a, a+ 六种模式,r 、r+
和a 三种模式文件必须已经存在,其他三种模式文件不存在就创建一个。本文用的
 r 模式,所以文件不存在会提示错误,而不是自动建立一个;
          permission是权限,举例说明:
有权限为:000 000 000 :第一组三位为user权限;第二组三位为同组其他用户的
权限;第三组三位为其他组所有人的权限。每个三位的权限依次代表读,写,执行。
如果有相应的权限就设置为一,没有设置为0。然后三位为组转成十进制数。

          文件打开后就可以使用其文件id,使用完后记得关闭文件
      ;# 打开配置文件
       set err [catch {set fileid [open $configFile r]} errMsg]

       if {$err == 1} {
              puts "errMsg : $errMsg"
              return $Value
       }
       ;# 成功打开文件后, 一行一行的加以分析
       set rowid 0                       ;#记录当前行数,程序调试时打印调
试信息使用的
       seek $fileid 0 start                        ;# 定位到文件头
       while {[eof $fileid] != 1} {                 ;# 读取文件内容
4.变量的自动增长使用命令 incr,也可以使用 set rowid [expr $rowid + 1],
显然前者更简捷

              incr rowid                          ;# 记录行数, 从一开始
    
              ;# 读出一行
              gets $fileid line
          
5.先期处理行,因为注释有两种情况,行中的注释和整行注释,先去掉注释,然后
去左右空格就可以得到真正需要的内容;如果先去空格,再去注释,由于行中的注
释和内容之间有空格,这样最后得到的内容在去掉行中注释后,会在后面留下一些
空格。所以需要先去掉注释,后去掉空格。
6.得到一个字符串在另一个字符串中首先出现的位置,使用函数string first
       格式:string first string1 string2
       讲解:返回string1在string2中第一次出现的位置;如果string1不在string2中,
返回-1
7.下面有一个细节是初学者经常犯错的地方,那就是:} else { ,这里必须严格
的 花括号,空格,else,空格,花括号,不能把花括号写到上一行或者下一行。
8.返回一个字符串的子串使用string range函数
       格式:string range string1 frompos topos
       讲解:取string1的从frompos到topos之间的字符串,注意这里字符串下标
是从零开始的,所以用string length string1得到的字符串的长度比字符串最后一
个字符的位置值要大一。如果取一个字符串中的某个字符就可以使用函数string index
string1 pos。
9.expr 和unix shell中一样,是进行数学运算的函数。
              ;# 先去掉注释, 再去掉两端的空格
              set commentpos [string first $Comment $line]        ;# 得到
注释符号的位置
              if { $commentpos == 0 } {
                     ;# 行以注释符号开头,忽略掉该行
              } else {

                     if { $commentpos != -1 } {        ;# 行中有注释符号
,去掉注释
                            set line [string range $line 0 [expr $commentpos-
1]]
                     }
                    
                     set line [string trim $line]          ;# 去掉两端的
空格
                     ;# puts "$rowid : line : $line"
                    
10.在tcl脚本中,循环中有break命令跳出循环,continue跳到循环的开头。不过
因为括号的使用,其实这里写不写continue都没有问题。
                     ;# 如果是空就继续循环
                     if { $line == "" } {
                            ;# 回循环

                            continue

                     } else {
                            ;# 先找section
                            set linelen [string length $line]         ;#字
符串长度
                            set lastpos [expr $linelen - 1 ]          ;#字
符串最后的位置
                            ;# puts "$rowid :len: $linelen   lastpos: $lastpos"

                            if { [string index $line 0] == "/[" && [string
index $line $lastpos] == "/]" } {
                                   ;# 如果是查找的section, 修改标志位;如
果不是相应的section, 需要将标志重新赋值
                                   if { [string range $line 1 [expr $lastpos
- 1 ]] == $Section } {  
                                          ;# puts "$rowid: find section
: $Section"
                                          set FindSection 1
                                   } else {
                                          set FindSection 0
                                   }
                            } else {
                                   ;# 已经找到了section才继续找key
                                   if { $FindSection == 1 } {
                                          set equalpos [string first $Equal
$line]   ;# 得到等号的位置
                                          if { $equalpos != -1} {
                                                 ;# 如果就是找寻的key,结
束循环
                                                 if { [string range $line
0 [expr $equalpos - 1]] == $Key } {
                                                        puts "$rowid: find
key"
                                                        set Value [string
range $line [expr $equalpos + 1] [string length $line]]
                                                        puts "$rowid: find
value: $Value"
                                                        break
                                                 }
                                          } else {
                                                 ;# 回循环
                                          }
                                   } else {
                                          ;# 回循环
                                   }
                            }
                     }
              }
       }
        ;# 关闭文件
       close $fileid
      
       return $Value
}
  set val ""
;# 测试正常情况下
set val [getConfig "config.ini" "section1" "key1"]
puts "val : $val"

;# 测试文件不存在的情况下
set val [getConfig "config1.ini" "section1" "key1"]
puts "val : $val"

  该程序在unix 环境下调试通过。
 
三.伪代码
为了便于理解程序,特写了下面的伪代码:

 

打开配置文件
       IF 出错 THEN
          过程结束
       END IF

 

文件打开成功,定位到文件头
WHILE 没有到文件尾
       读出一行

 

       处理得到的行,先去掉行中的注释,再去掉行左右的空格
      
       IF 行为空 THEN
       ELSE
              行不为空
              IF [开头 ]结束 THEN
                     得到SECTION
                     IF 得到的SECTION就是要找的SECTION THEN
                            置标志位
                     ELSE
                            清空标志位
                     END IF
              ELSE
                     IF 已经找到SECTION了 THEN
                            IF 行中有等号 THEN
                                   IF 等号左边的是在查找的KEY THEN
                                          取得等号右边的值,跳出循环
                                   ELSE
                                          等号左边不是在查找的KEY,继续下
一行
                                   END IF
                            ELSE
                                   行中没有等号,继续下一行
                            END IF
                     ELSE
                            还没有找到SECTION,继续下一行
                     END IF
              END IF
       END IF

 

关闭文件
返回值
                   
  四.讨论
这个程序本身比较简单,主要目的是想通过这个程序让tcl初学者对tcl有个感性认
识。真正在工作使用中,这个脚本需要改进的地方还有很多。
比如配置文件比较小的话,可以读一个配置项打开一次文件。当配置文件大了以后,
这种方法显然效率比较低,可能需要一次将文件的内容全部读入一个列表中,然后
进行读取,这样速度会有提高。再比如错误处理只是在过程中将错误信息显示到屏
幕上,是不是应该定义一个错误信息的全局变量来传出过程,或者使用upvar 直接
修改错误信息变量。否则返回的值是空,并不能确定是配置的值的确是空还是执行
中出现了错误。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值