读取文本文件,识别模式并将其值替换为Tcl

时间:2014-09-15 14:18:49

标签: regex file tcl

我对tcl过程感兴趣,该过程读​​取文本文件,标识模式并替换该模式的值。 假设“数据”是需要的模式,当前值是1 2 3,如何将每个“数据”实例的值更新为值3 2 1?我曾多次使用fileutil包,但我没有设法替换这些值。 有什么建议?

1 个答案:

答案 0 :(得分:2)

此任务不需要fileutil,Tcl核心命令也可以。但是,让我们使用fileutil

您的代码是

package require fileutil

set pattern {data}
set filename test.txt

proc processContents {fileContents} {
    return [string map {(1 2 3) {3 2 1}}] $fileContents
}

fileutil::updateInPlace test.txt processContents

这是一个公平的开始,但由于几个原因不起作用。

一个问题是return的参数的结尾括号应该在$fileContents的右边,而不是在它的左边。

另一个问题是提供给string map 的替换列表不说“替换(1 2 3)与3 2 1”,它说“替换(1与2和3)与3 2 1“。这是因为如果你是美国人,那么圆括号或圆括号或圆括号在这里没有任何句法意义,它们只是文本。 2周围的未加引号的空格意味着这一段文本是三个列表元素,而不是一个。要使(1 2 3)成为单个列表元素,您需要引用空格,如下所示:

(1\ 2\ 3)
"(1 2 3)"
{(1 2 3)}

我怀疑你不想要括号,所以这行应该是:

    return [string map {{1 2 3} {3 2 1}} $fileContents]

现在,为updateInPlace调用一个单独的命令通常是一个好主意,如果处理很复杂(基本上是多个命令),你需要这样做。但在这种情况下,您只执行一个简单的操作。 Tcl语法的优点在于可以轻松组装命令。如果给updateInPlace一半命令,它将通过向其添加文件内容来完成命令,执行命令并用结果替换文件的内容。

假设您有一个只包含字符串foo bar的文件。如果你想1)打印出来并且2)清除文件的内容,你可以调用

fileutil::updateInPlace test.txt puts

updateInPlace命令将读取文件的内容并将其附加到单词puts,中间有空格,从而产生调用puts {foo bar}。将打印该字符串,并将结果(即空字符串)写回文件,替换之前的内容。

您提供的命令的一半可以有其他参数,只要文件的内容适合作为最后一个参数。例如,您可以将内容复制到其他位置,另一个打开的文件或通过套接字:

fileutil::updateInPlace test.txt "puts $channel"

将内容发送到频道标识符$channel所连接的位置。

你可能猜到我要去哪里。

fileutil::updateInPlace test.txt {string map {{1 2 3} {3 2 1}}}

是您执行所需处理所需的全部内容。

如果您希望能够轻松更改匹配的模式并仍然用其反向替换它,您可以这样做:

set pattern {1 2 3}
fileutil::updateInPlace test.txt [list string map [list $pattern [lreverse $pattern]]]

除此之外:你什么时候用括号引用命令,什么时候应该使用双引号,为什么要使用list命令?

简而言之:如果您需要替换命令字符串中的任何内容,请使用引号或list。如果不这样做,您可以使用大括号。如果您需要进行替换但仍保留列表结构,则必须使用list。所以:

puts              ;# one word: you don't need to use any of the above
{puts}            ;# but any one of them will do
{puts -nonewline} ;# two words: you need to wrap it: any method will do
"puts $f"         ;# two words, need to substitute: quotes or list will do

您可以通过打印来检查命令的外观:

puts "string map {$pattern [lreverse $pattern]}"
# => string map {1 2 3 3 2 1}

这里丢失了必要的列表结构。失败。 (这就是我之前写的。)

puts [list string map [list $pattern [lreverse $pattern]]]
# => string map {{1 2 3} {3 2 1}}

保留列表结构。得分了! (由Donal Fellows修正。)


不使用fileutil(并且没有错误处理):

set f [open test.txt r]
set fileContents [chan read -nonewline $f]
chan close $f
set f [open test.txt w]
chan puts -nonewline $f [string map {{1 2 3} {3 2 1}} $fileContents]
chan close $f

由于操作更基本,因此版本更长。此脚本打开文件两次:一次用于读取,将内容放入变量fileContents,一次用于写入,将转换后的内容放回文件中。

稍微冗长一点:

set f [open test.txt r+]
chan puts -nonewline $f [string map {{1 2 3} {3 2 1}} [chan read -nonewline $f][chan seek $f 0;list]]
chan close $f

此版本打开文件进行读写。内容永远不会存储在任何变量中,而是直接从chan read通过string map传送到chan puts,后者将转换后的内容写回文件。 (chan seek $f 0;list位是一个技巧,可以将写入位置重新设置到文件的开头。)


文档:chanfileutillistopenpackageprocputs,{{3 }},returnset