【问题标题】:Split a line from a file to replace elements in tcl从文件中拆分一行以替换 tcl 中的元素
【发布时间】:2016-03-27 18:01:33
【问题描述】:

我正在尝试执行以下操作:

  1. 打开要读取的文件
  2. 分割线
  3. 识别感兴趣的线
  4. 将名称Prefix_2 替换为Prefix_999
  5. 删除所有带有Prefix_1的行
  6. 将行保存到原始文件

我可以做到第 3 点,但是在替换名称时,我似乎无法将感兴趣的行拆分为元素,以便可以将 Prefix_2 替换为 Prefix_999

它总是替换为整个感兴趣的行,如下所示,例如

Prefix_2.Var1 = 5124 将替换为 Prefix_999 而不是 Prefix_999.Var1 = 5124

文件Data.txt中的示例数据

Prefix_1.Var1 = 200
Prefix_1.Var2 = 0.3
Prefix_1.Var3 = 0.5
Prefix_1.Var4 = 0.25
Prefix_1.Var5 = 3
Prefix_1.Var6 = 36
Prefix_1.Var7 = 5612
Prefix_1.Var8 = 631
Prefix_1.Var9 = 102
Prefix_1.Var10 = 14
Prefix_1.Var11 = 3
Prefix_1.Var12 = 2
Prefix_2.Var1 = 5124
Prefix_2.Var2 = 876
Prefix_2.Var3 = 10.1
Prefix_2.Var4 = 11

我的代码:

set input [open "Data.txt" r]

set number 0
set var2 ""
while { [gets $input line] >= 0 } {
    incr number
    set sline [split $line "\n"]
    set var1 [regexp {Prefix_2.Var1} $sline match]
    if {$var1 == 1} {
        set rVar1 [split $sline \s]
        # check the length to index correctly for replacing
        set rVar2 [llength $rVar1]
        set rVar2 [lreplace $rVar2 0 0 Prefix_999.Var1]
    }
}

close $input

【问题讨论】:

  • 您正在替换变量rVar2,该变量具有实际预期数据的长度。不是实际列表。

标签: regex tcl


【解决方案1】:

由于您不是在编写文本过滤器,因此您最好在更高的级别上工作。

proc processLine {bufName line} {
    upvar 1 $bufName buf
    switch -regexp $line {
        {^Prefix_1\.} {}
        {^Prefix_2\.} {
            lappend buf [regsub 2 $line 999]
        }
        default {
            lappend buf $line
        }
    }
}

package require fileutil

set buf {}
fileutil::foreachLine line data.txt {
    processLine buf $line
}
fileutil::writeFile data.txt [join $buf \n]

较低级别的open-gets-close组合是语言核心的一部分; fileutil 包可用于抽象出此组合的大多数用法,它是 Tcl 的配套库 Tcllib 的一部分。

我正在处理您的规范,因为您的代码有点难以理解。

  1. 打开要读取的文件
  2. 分割线
  3. 识别感兴趣的线
  4. 将名称前缀_2 替换为前缀_999
  5. 删除所有带有 Prefix_1 的行
  6. 将行保存到原始文件

识别感兴趣的线

processLine 命令将接收参数line 中的每一行。 switch -regexp $line {...} 命令让我可以通过前缀(或其他任何方式,标准可以是任何可以用正则表达式表达的内容)对行进行分类。共有三类行:以“Prefix_1”开头的行、以“Prefix_2”开头的行以及所有其他行。我将其设置为将行保存在持久缓冲区中的默认操作(在命令内部称为 buf,在命令外部称为 $bufName:在我的代码中,两个名称相同)。

将名称前缀_2 替换为前缀_999

对于以“Prefix_2”开头的行,我会跳过默认操作,而是保存修改后的行,将第一次出现的 2 替换为 999。

删除所有带有 Prefix_1 的行

只需跳过这些行的默认操作即可满足此要求。

打开文件以读取和分割线

这是由fileutil::foreachLine自动完成的。

将行保存到原始文件

我只需将缓冲区中的行与换行符连接起来,然后调用fileutil::writeFile 来更新文件。


ETA没有upvar的版本

行处理命令不必直接访问行缓冲区;相反,它可以简单地返回行(跳过的行是空字符串)。在这种情况下,需要在 foreachLine 的脚本参数中执行组装行缓冲区:

proc processLine line {
    switch -regexp $line {
        {^Prefix_1\.} {}
        {^Prefix_2\.} {
            regsub 2 $line 999
        }
        default {
            return $line
        }
    }
}

package require fileutil

set buf {}
fileutil::foreachLine line data.txt {
    set line [processLine $line]
    if {$line ne {}} {
        lappend buf $line
    }
}
fileutil::writeFile data.txt [join $buf \n]

(我认为这个版本很差。)

文档:fileutil 包,if,join,lappend,package,proc,regsub,set,switch,upvar

【讨论】:

  • 我喜欢使用foreachLine和writeFile;他们解决了 I/O 问题。您应该提到 fileutil 包是 Tcllib 的一部分,以防有人更容易找到它。
  • 谢谢彼得,这很好用。但是我仍在尝试理解 buf 和 bufName。但是我在删除行时遇到了另一个问题......有一些具有相同前缀的变量是表而不是常量。如果需要,我会在另一篇文章中提出这个问题。
  • @Anju:我添加了一个不需要upvar 的版本,但是变量链接工具非常有用,这是 Tcl 优于大多数其他语言的功能之一,所以我建议您花时间了解它的工作原理。关于您的其他问题,另一篇文章可能是提出该问题的最佳方式。
【解决方案2】:

您可以将regsub 用于您的目的。

% set line {Prefix_2.Var1 = 5124}           
Prefix_2.Var1 = 5124 
% regsub {Prefix_2} $line {Prefix_999} 
Prefix_999.Var1 = 5124
%

【讨论】:

    猜你喜欢
    • 2011-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-20
    • 1970-01-01
    相关资源
    最近更新 更多