用tcl语言操作文件

时间:2016-03-28 14:35:34

标签: tcl

第一次发布海报和TCL新手,请原谅我的知识。

我在stackoverflow上找到了一些示例,并通过该帮助创建了一个脚本。

我需要修改文件的几行,我尝试过以下内容(参见代码)。我似乎可以添加感兴趣的行,但它不会在正确的位置写入,例如如果我想要替换第3行,它会在第3行之后添加 而且,如果有多个行操作,则删除后续行。

最后,有人可能会建议使用名称而不是行号来识别感兴趣的行的最佳方法。名称始终采用Filter.HpOrd_n =格式 其中n0...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

3 个答案:

答案 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包,foreachifincrjoinlindexlrange,{{ 3}},lsearchlsetpackageprocregsubseekset

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