1

我正在尝试实现一个 tcl 脚本,它读取一个文本文件,并屏蔽包含它的所有敏感信息(如密码、IP 地址等)并将输出写入另一个文件。

截至目前,我只是用* * 或 ##### 替换这些数据,并用正则表达式搜索整个文件以找到我需要屏蔽的内容。但是由于我的文本文件可以是 100K 行或更多的文本,结果证明这是非常低效的。

是否有任何内置的 tcl 函数/命令可以用来更快地执行此操作?是否有任何附加软件包提供额外的选项来帮助完成这项工作?

注意:我使用的是 tcl 8.4(但如果在较新版本的 tcl 中有方法可以做到这一点,请给我指点)

4

3 回答 3

2

一般来说,您应该将您的代码放在一个过程中,以便从 Tcl 中获得最佳性能。(您在 8.5 和 8.6 中还有一些相关选项,例如 lambda 术语和类方法,但它们与过程密切相关。)您还应该注意一些其他事情:

  • 将您的表达式放在大括号中(expr {$a + $b}而不是expr $a + $b),因为这样可以实现更有效的编译策略。
  • 仔细选择您的频道编码。(如果这样做fconfigure $chan -translation binary,该通道将传输字节而不是字符。但是,gets在 8.4 中,在面向字节的通道上效率不是很高。使用-encoding iso8859-1 -translation lf将提供大部分好处。)
  • Tcl 很好地进行了通道缓冲。
  • 可能值得用不同版本的 Tcl 对您的代码进行基准测试,看看哪个效果最好。如果您不想为了测试而安装多个 Tcl 解释器(次要)麻烦,请尝试使用tclkit构建进行测试。

进行面向行的转换的惯用方法是:

proc transformFile {sourceFile targetFile RE replacement} {
    # Open for reading
    set fin [open $sourceFile]
    fconfigure $fin -encoding iso8859-1 -translation lf

    # Open for writing
    set fout [open $targetFile w]
    fconfigure $fout -encoding iso8859-1 -translation lf

    # Iterate over the lines, applying the replacement
    while {[gets $fin line] >= 0} {
        regsub -- $RE $line $replacement line
        puts $fout $line
    }

    # All done
    close $fin
    close $fout
}

如果文件足够小,可以轻松放入内存中,则效率更高,因为整个匹配替换循环被提升到 C 级别:

proc transformFile {sourceFile targetFile RE replacement} {
    # Open for reading
    set fin [open $sourceFile]
    fconfigure $fin -encoding iso8859-1 -translation lf

    # Open for writing
    set fout [open $targetFile w]
    fconfigure $fout -encoding iso8859-1 -translation lf

    # Apply the replacement over all lines
    regsub -all -line -- $RE [read $fin] $replacement outputlines
    puts $fout $outputlines

    # All done
    close $fin
    close $fout
}

最后,正则表达式不一定是进行字符串匹配的最快方法(例如,string match它要快得多,但接受更受限制的模式类型)。将一种样式的替换代码转换为另一种样式并让它真正快速运行并不是 100% 微不足道的(RE 非常灵活)。

于 2013-03-19T14:03:55.703 回答
1

一个有 100K 行的文件并不多(除非每行都是 1K 字符长:) 所以我建议你read将整个文件放入一个 var 并在该 var 上进行替换:

set fd [open file r+]
set buf [read $fd]
set buf [regsub -all $(the-passwd-pattern) $buf ****]
# write it back
seek $fd 0; # This is not safe! See potrzebie's comment for details.
puts -nonewline $fd $buf
close $fd
于 2013-03-19T08:08:01.967 回答
1

特别是对于非常大的文件 - 如前所述 - 这不是将整个文件读入变量的最佳方法。一旦您的系统内存不足,您就无法防止应用程序崩溃。对于以换行符分隔的数据,最简单的解决方案是缓冲一行并进行处理。

只是给你一个例子:

# Open old and new file
set old [open "input.txt" r]
set new [open "output.txt" w]
# Configure input channel to provide data separated by line breaks
fconfigure $old -buffering line
# Until the end of the file is reached:
while {[gets $old ln] != -1} {
    # Mask sensitive information on variable ln
    ...
    # Write back line to new file
    puts $new $ln
}
# Close channels
close $old
close $new

我想不出任何更好的方法来处理 Tcl 中的大文件 - 请随时告诉我任何更好的解决方案。但是 Tcl 并不是用来处理大数据文件的。为了获得真正的性能,您可以使用编译而不是脚本编程语言。

编辑:![eof $old]在while循环中替换。

于 2013-03-19T08:18:29.250 回答