在文件中搜索数字,增量和替换

时间:2017-10-23 20:07:53

标签: tcl

我有一个VHDL文件,其行如下:

constant version_nr     :integer := 47;

我想在文件中增加此行中的数字。有没有办法用TCL来实现这个目标?

1 个答案:

答案 0 :(得分:2)

这主要是一个字符串操作。棘手的一点是找到要操作的线并从中挑出数字。这可能偶尔会很尴尬,但主要是选择合适的正则表达式(因为这是他们擅长的解析任务)。进行匹配的原始RE是:

^\s*constant\s+version_nr\s*:integer\s*:=\s*\d+\s*;\s*$

这实质上是将空白序列的所有可能位置转换为\s*(除非必须使用空格,后者变为\s+)并将数字与\d+匹配,即数字序列。然后我们在括号中添加 capture 有趣的子串,它们是前缀,数字和后缀:

^(\s*constant\s+version_nr\s*:integer\s*:=\s*)(\d+)(\s*;\s*)$

现在我们已经足够进行转换(我们将作为一个程序执行,因此我们可以给它一个好名字):

proc lineTransform {line} {
    set RE {^(\s*constant\s+version_nr\s*:integer\s*:=\s*)(\d+)(\s*;\s*)$}
    if {[regexp $RE $line -> prefix number suffix]} {
        # If we match, we increment the number...
        incr number
        # And reconcatenate it with the prefix and suffix to make the new line
        set line $prefix$number$suffix
    }
    return $line
}

在Tcl 8.7(您还没有使用)中,您可以将其写成更简洁的形式:

proc lineTransform {line} {
    # Yes, this version can be a single (long) line if you want
    set RE {^(\s*constant\s+version_nr\s*:integer\s*:=\s*)(\d+)(\s*;\s*)$}
    regsub -command $RE $line {apply {{- prefix number suffix} {
        # Apply the increment when the RE matches and build the resulting line
        string cat $prefix [incr number] $suffix
    }}}
}

现在我们有了一个行变换,我们只需将它应用于文件的所有行。这可以通过适合内存的文件(最多几百MB)轻松完成,但需要对较大文件采取额外措施,因为您需要从一个文件流式传输到另一个文件:

proc transformSmallFile {filename} {
    # Read data into memory first
    set f [open $filename]
    set data [read $f]
    close $f

    # Then write it back out, applying the transform as we go
    set f [open $filename w]
    foreach line [split $data "\n"] {
        puts $f [transformLine $line]
    }
    close $f
}

proc transformLargeFile {filename} {
    set fin [open $filename]
    # The [file tempfile] command makes working with temporary files easier
    set fout [file tempfile tmp [file normalize $filename]]

    # A streaming transform; requires that input and output files be different
    while {[gets $fin line] >= 0} {
        puts $fout [transformLine $line]
    }

    # Close both channels; flushes everything to disk too
    close $fin
    close $fout

    # Rename our temporary over the original input file, replacing it
    file rename $tmp $filename
}