读取4列文件,其中两列在tcl中匹配

时间:2017-03-19 13:07:50

标签: tcl

我通过源代码文件在文本文件中记录移动节点的坐标。我想从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
    }  
}

2 个答案:

答案 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

所以,为什么你会犯错误仍然是一个谜。