我通过源代码文件在文本文件中记录移动节点的坐标。我想从tcl脚本中读取此文本文件,以访问特定节点的最近记录的坐标。以下是我尝试过的代码,但它没有返回任何内容。
proc accessLoc { attime node_id} {
set fb [open "node_info.txt" r]
set filecontent [read $fb]
set input_list [split $filecontent "\n"]
set data [lsearch -all -inline $input_list "$node_id"]
foreach elem $data {
if {[lindex $elem 0] eq {attime} && {[lindex [split $elem " "]1] eq {node_id}} {
set xCoord [lindex [split $elem " "]2]
set yCoord [lindex [split $elem " "]3]
return [list xCoord yCoord]
}
close $fb
}
}
答案 0 :(得分:0)
好吧,首先,您发现了一个小错误,因为您已将close
置于错误的位置。由于数据相对较小且允许您使用read
,因此您可以立即 close
,以便文件描述符不会泄露。让我们现在解决这个问题。
proc accessLoc { attime node_id} {
set fb [open "node_info.txt" r]
set filecontent [read $fb]
close $fb
set input_list [split $filecontent "\n"]
set data [lsearch -all -inline $input_list "$node_id"]
foreach elem $data {
if {[lindex $elem 0] eq {attime} && {[lindex [split $elem " "]1] eq {node_id}} {
set xCoord [lindex [split $elem " "]2]
set yCoord [lindex [split $elem " "]3]
return [list xCoord yCoord]
}
}
}
接下来,让我们找不到你想要的错误,而不是让它什么都不返回。这将给你一个明确的失败指示,这更令人困惑。 (您可以在更广泛的上下文中catch
error
,或者从Tcl 8.6开始使用try
以获得更精细的内容。)
proc accessLoc { attime node_id} {
set fb [open "node_info.txt" r]
set filecontent [read $fb]
close $fb
set input_list [split $filecontent "\n"]
set data [lsearch -all -inline $input_list "$node_id"]
foreach elem $data {
if {[lindex $elem 0] eq {attime} && {[lindex [split $elem " "]1] eq {node_id}} {
set xCoord [lindex [split $elem " "]2]
set yCoord [lindex [split $elem " "]3]
return [list xCoord yCoord]
}
}
error "did not find the item"
}
你也可能不需要lsearch
;扫描数据并不 昂贵。这让我可以进一步缩短代码:
proc accessLoc { attime node_id} {
set fb [open "node_info.txt" r]
set filecontent [read $fb]
close $fb
foreach elem [split $filecontent "\n"] {
if {[lindex $elem 0] eq {attime} && {[lindex [split $elem " "]1] eq {node_id}} {
set xCoord [lindex [split $elem " "]2]
set yCoord [lindex [split $elem " "]3]
return [list xCoord yCoord]
}
}
error "did not find the item"
}
但那不是真正的问题。你在这些方面遇到了问题,这些问题看起来并不像我所期望的那样,因为它们指的是文字并使用lindex
的不寻常形式(列表和索引之间没有空格?不太可能做你期望的事情!):
if {[lindex $elem 0] eq {attime} && {[lindex [split $elem " "]1] eq {node_id}} {
set xCoord [lindex [split $elem " "]2]
set yCoord [lindex [split $elem " "]3]
return [list xCoord yCoord]
相反,我希望更像这样的事情:
lassign [split $elem " "] thisAtTime thisId xCoord yCoord
if {$thisAtTime eq $attime && $thisId eq $node_id} {
return [list $xCoord $yCoord]
我们可以使用split
一次分割整行,并将其分解为具有名称的字段(错误,局部变量)非常方便,lassign
就是为此而设计的。然后,我们可以简化比较表达式(记住变量在读取时应该以{{1}}作为前缀)并且以下$
也会受益。让我们把它全部放在上下文中。
return
答案 1 :(得分:0)
(这不是一个真正的答案,更多是对Donal Fellows的评论'答案,为了便于阅读而作为答案发布。)
如果你不能使用lassign
而你想获得潜在的几个匹配,那么你应该这样做:
proc accessLoc {attime node_id} {
set result NONE
# due to Donal Fellows
set fb [open "node_info.txt" r]
set filecontent [read $fb]
close $fb
foreach elem [split [string trim $filecontent] "\n"] {
# Leave exactly one of the two following lines uncommented
# lassign [split $elem " "] thisAtTime thisId xCoord yCoord
foreach {thisAtTime thisId xCoord yCoord} [split $elem " "] break
if {$thisAtTime eq $attime && $thisId eq $node_id} {
set result [list $xCoord $yCoord]
}
}
if {$result eq "NONE"} {
error "did not find the item"
} else {
return $result
}
}
要获得最后一个匹配,它会将每个匹配保存在变量result
中,这意味着在扫描结束时变量的值是最后一个匹配。
我无法使此代码无法在给定的数据文件中找到一行。例如:
% accessLoc 71.729787 0
300.0000 150.0000
所以,为什么你会犯错误仍然是一个谜。