读取tcl中两列条目匹配的4列文件

读取tcl中两列条目匹配的4列文件,tcl,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

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

首先,您有一个小错误,您将
关闭
放在了错误的位置。由于数据相对较小,允许您使用
读取
,因此您可以而且应该在之后立即关闭,以避免文件描述符泄漏。我们现在来解决这个问题

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
一个
错误
,或者从Tcl 8.6开始使用
try
以获得更微妙的效果。)

您也可能不需要
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
(列表和索引之间没有空格?不太可能达到您所期望的效果!):

相反,我期待的是更像这样的事情:

        lassign [split $elem " "] thisAtTime thisId xCoord yCoord
        if {$thisAtTime eq $attime && $thisId eq $node_id} {
            return [list $xCoord $yCoord]
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
    }
}
我们可以使用
split
一次拆分整行,将其拆分为带名称的字段(err,局部变量)非常方便,而
lassign
就是为此而设计的。然后,我们可以简化比较表达式(记住变量在读取时应以
$
作为前缀),下面的
返回值也有好处。让我们把它放在上下文中

proc accessLoc { attime node_id} {
    set fb [open "node_info.txt" r]
    set filecontent [read $fb]
    close $fb
    foreach elem [split $filecontent "\n"] {
        lassign [split $elem " "] thisAtTime thisId xCoord yCoord
        if {$thisAtTime eq $attime && $thisId eq $node_id} {
            return [list $xCoord $yCoord]
        }
    }
    error "did not find the item"
}
(这不是一个真正的答案,更多的是对Donal Fellows答案的评论,出于可读性考虑,作为答案发布。)

如果您不能使用
lassign
,并且希望获得可能的多个匹配项的最后一个匹配项,则应执行以下操作:

        lassign [split $elem " "] thisAtTime thisId xCoord yCoord
        if {$thisAtTime eq $attime && $thisId eq $node_id} {
            return [list $xCoord $yCoord]
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

所以,你为什么会出错仍然是个谜。

@谢谢你详细的回答。我尝试过这个解决方案,但遇到了一个错误“无效的命令名'lassign'。然而,我在网上发现,这可能是旧tcl版本的一个问题。我正在运行8.6TCL版本,但仍然出现此错误。你能告诉我可能是什么问题,因为我无法解决。可能是安装不好?您能告诉我们什么
使$tcl\u pathlevel打印?同时,一个快速修复方法是用一个古老的习惯用法替换
lassign
的调用:
foreach{thisAtTime thisId xCoord yCoord}[split$elem”“]break
@Peter its prints“无法读取“tcl_pathlevel”:没有这样的变量”。你的意思是“卖出$tcl_补丁级别”吗?如果是,则打印“8.6.5”。虽然文件中存在这样一个条目,但它抛出了错误“未找到该项”。我在等待你的宝贵建议。@Mary:是的,对不起,我的意思是
tcl\u patchlevel
。好的,我完全不知道为什么
lassign
不起作用。至少我已经确认该过程与我建议的
foreach
调用一起工作;至少如果外部
foreach
中的
$filecontent
更改为
[string trim$filecontent]
。如果它对您不起作用,您可能需要花很长的时间来验证分配给
elem
thisAtTime
等的值是否得到了它们应该得到的值。如果你从你的数据文件中写几行,我可以在这里试试。@Peter:当然可以。下面是我的“node_info.txt”文件中的几行。我想得到一个特定节点的最新x和y坐标,它(显然)位于文件的末尾。它应该忽略该特定节点的所有上述条目。它应该匹配时间和节点,然后返回该节点的x和y坐标。请添加节点信息中的示例内容。txt@Sharad我在下面分享了它。:)