我对tcl过程感兴趣,该过程读取文本文件,标识模式并替换该模式的值。
假设“数据”是需要的模式,当前值是1 2 3,如何将每个“数据”实例的值更新为值3 2 1?我曾多次使用fileutil
包,但我没有设法替换这些值。
有什么建议?
答案 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
位是一个技巧,可以将写入位置重新设置到文件的开头。)
文档:chan,fileutil,list,open,package,proc,puts,{{3 }},return,set