第一次发布海报和TCL新手,请原谅我的知识。
我在stackoverflow上找到了一些示例,并通过该帮助创建了一个脚本。
我需要修改文件的几行,我尝试过以下内容(参见代码)。我似乎可以添加感兴趣的行,但它不会在正确的位置写入,例如如果我想要替换第3行,它会在第3行之后添加 而且,如果有多个行操作,则删除后续行。
最后,有人可能会建议使用名称而不是行号来识别感兴趣的行的最佳方法。名称始终采用Filter.HpOrd_n =
格式
其中n
为0...k
info.dat
AA
BB
Filter.HpOrd_1 = 2
Filter.HpOrd_2 = 2
Filter.HpOrd_3 = 0.1
Filter.HpOrd_4 = 0.2
CC
DD
EE
FF
代码:
set fd [open "info.dat" r+]
set i 0
while { [gets $fd line] != -1 } {
set line [split $line "\n"]
incr i
if {$i == 3} {
set nLine [lreplace $line 0 0 Filter.LoPass]
puts $fd [join $nLine "\n"]
}
if {$i == 6} {
set nLine [lreplace $line 0 0 Filter.Butterworth]
puts $fd [join $nLine "\n"]
}
}
close $fd
答案 0 :(得分:0)
TCL只是一种元语言,set fd [open "info.dat" r+]
与一般文件描述符处理有关。如果打开文件描述符" r +"您可以读取和写入该文件描述符,但是一个文件描述符始终指向文件中的一个点。
使用" r +"您的文件描述符最初指向文件的开头。然后你gets $fd line
来自文件的一行,所以$fd
指向之后的第二行的开头。现在你puts $fs [join $nline "\n"]
从第二行的开头盲目地覆盖,依此类推。
通常,您无法替换一个文件中的行,但是您将写入第二个文件并在关闭这两个文件后移动它。您可以使用seek覆盖,但是从文件中的某个点覆盖。所以你放的东西应该总是有相同的尺寸,你以前读过的。
答案 1 :(得分:0)
普通文件(基本上所有编程语言)都是面向字符/字符而不是面向行。这意味着1)您需要使用seek
操作返回到要覆盖的行的开头,并且2)除非新行与旧行的长度完全相同,您将体验到它周围的短线。
您还有其他问题。 set line [split $line "\n"]
没有做任何事情:您只是从line
阅读gets
,因此保证不会有任何换行符。 [join $nLine "\n"]
没有做你认为它做的事情:它会用$line
替换任何单个换行符的空白序列,但不会在字符串的末尾添加任何换行符。< / p>
除非您的文件非常庞大,否则我建议使用以下内容:
替换为行号
proc lineReplace args {
set lines [split [lindex $args end] \n]
foreach {n line} [lrange $args 0 end-1] {
set index [incr n -1]
if {$index > 0} {
lset lines $index $line
}
}
join $lines \n
}
package require fileutil
fileutil::updateInPlace info.dat {
lineReplace
3 Filter.LoPass
6 Filter.Butterworth
}
在&#34;前端&#34;您只需指定要使用的命令,然后指定行号/新行文本对。
在&#34;后端&#34; (lineReplace
命令)参数args
将包含那些数字/行对,并在最后作为单个项目包含文件的完整内容。然后将文件内容拆分为行列表,并为每个数字/行对替换该列表中的一个项目。最后,行列表连接回一个字符串,每行之间有换行符。此字符串由lineReplace
返回到fileutil::updateInPlace
,它用返回的字符串替换文件中的旧内容。
按名称替换
proc lineReplaceByName args {
set lines [split [lindex $args end] \n]
foreach {name line} [lrange $args 0 end-1] {
set index [lsearch $lines $name*]
if {$index > 0} {
lset lines $index $line
}
}
join $lines \n
}
fileutil::updateInPlace info.dat {
lineReplaceByName
Filter.HpOrd_1 Filter.LoPass
Filter.HpOrd_4 Filter.Butterworth
}
在这种情况下,&#34;后端&#34;通过在每行的开头搜索给定的名称来计算行号。如果找不到该名称,则跳过替换操作。否则它和以前一样。
更换名称
如果您不想更换整行,而只是替换它的名称部分,则需要进行一些更改。如果你100%确定1)名称中没有任何空格,2)名称和=
之间总是有空格,你可以用{{1}替换lset lines $index $line
}。如果您想更安全地玩,可以用
lset lines $index 0 $line
使用正则表达式查找lset lines $index [regsub {.+(?=\s*=\s*)} [lindex $lines $index] $line]
字符前面的字符区域(可选择周围有空格),然后将其替换为您提供的文本。
=
包是Tcl的fileutil
伴随库的一部分。
文档:fileutil包,foreach,if,incr,join,lindex,lrange,{{ 3}},lsearch,lset,package,proc,regsub,seek,set
答案 2 :(得分:0)
使用普通Tcl:
# the input and output file handles
set fin [open info.dat r]
set fout [file tempfile fname]
# process the file
while {[gets $fin line] != -1} {
puts $fout [string map {
"Filter.HpOrd_1" "Filter.LoPass"
"Filter.HpOrd_4" "Filter.Butterworth"
} $line]
}
close $fin
close $fout
# backup the original and overwrite it
file link -hard info.dat.bak info.dat
file rename -force -- $fname info.dat