我需要编写一个tcl脚本来处理文本文件的行。文件看起来像
10.77.33.247 10.77.33.241 rtp 0x26
10.77.33.247 10.77.33.241 rtp 0x26
10.77.33.247 10.77.33.241 rtp 0x26
10.77.33.247 10.77.33.241 0x24
10.77.33.247 10.77.33.241 0x22
10.77.33.247 10.77.33.241 0x21
我需要能够遍历文件,并且对于包含
rtp
的每一行,将其后面的值(例如,上面的示例中的0x26
)存储在一个变量中,以便在脚本的其他部分中使用。 最佳答案
这里有一个(相当低级的)tcl的方法。
set ch [open myfile.txt]
set data [chan read $ch]
chan close $ch
set lines [split [string trim $data] \n]
set res {}
foreach line $lines {
if {[llength $line] > 3 && [lindex $line 2] eq {rtp}} {
lappend res [lindex $line 3]
}
}
如果将“myfile.txt”替换为数据文件的名称并运行此代码,则会得到在变量
res
中收集的单词。解释
通常最好使用标准(内置或tcllib)命令,比如glenn jackman的答案中的
fileutil::foreachLine
。不过,如果一个人想一步一步地去做,tcl仍然让这件事变得非常容易。第一步是将文件的内容放入内存。也有一个标准命令用于此操作:
fileutil::cat
,但以下顺序可以:set ch [open myfile.txt]
set data [chan read $ch]
chan close $ch
(这或多或少相当于
set data [fileutil::cat myfile.txt]
)下一步是把文本分成几行。删除文本两端的空白总是一个好主意,否则松散的换行符会创建干扰处理的空元素。
set lines [split [string trim $data] \n]
在某些情况下,我们可能不得不将行拆分为字段列表,但从示例来看,这些行似乎已经可以用作列表(只有空格、字母数字和良好表现的标点符号(如点)的行通常是这样的)。
我们需要一个匹配线路的测试。有几种方法适合您提供的示例数据,包括
string match *rtp* $line ;# match every line that has "rtp" somewhere
[llength $line] > 3 ;# match every line that has more than three columns
[lindex $line 2] eq {rtp} ;# match every line where the third element is "rtp"
我们还需要一种方法来提取我们想要的数据。如果“rtp”后面的单词总是在最后一列,
[lindex $line end]
将完成这项工作。如果单词总是在第四列,但可能还有更多的列,[lindex $line 3]
更好。抓住其中的两个选项,可以编写获取指定单词列表的过程
set res {}
foreach line $lines {
if {[llength $line] > 3 && [lindex $line 2] eq {rtp}} {
lappend res [lindex $line 3]
}
}
(在伪代码中:获取一个空列表(
res
);测试每一行(使用上面两个测试的组合),从每一个匹配行中提取所需的单词并将其添加到res
列表中。)或者,使用
lmap
(tcl 8.6+)set res [lmap line $lines {
if {[llength $line] > 3 && [lindex $line 2] eq {rtp}} {
lindex $line 3
} else {
continue
}
}]
“rtp”单词后面的所有单词现在都应该在
res
中。如果你只想要最后一场比赛,那就是[lindex $res end]
。文档:chan,continue,foreach,if,lappend,lindex,llength,lmap,open,set,split,string,,