Tcl中的模式匹配

3

我有一个文件somefile.txt,其中包含如下行:

{ abc1 } 1
{ cde1 } 101
{ fgh1 } 1
{ ijk1 } 2 

这是一个非常大的文件。我只想找出第一行和第三行并计数。我已经尝试使用正则表达式和lsearch(将其转换为列表)通过 {\s\}\s1\n} ,但不起作用。我该怎么办...?

我也尝试过{\s\}\s1} ,但它会打印所有4行。


你的问题非常不清楚:你想看到什么输出? - glenn jackman
那么,你想要计算1+1的和吗? - Wiktor Stribiżew
好的,我只想看到那些以1结尾的行(只有1,不是111、101或100),在花括号外面。有数百万行,我这里只展示了四行,在引号字符“”、[]、/内部,例如{ a/b"[c] } 1也存在。 - ShivankG
3个回答

1

解决方案1:如果您不想使用regexp,并且您的输入行具有与{string} number相同的格式

set fd [open "somefile.txt" r]
while {[gets $fd line] >= 0} {
    if {[lindex $line 1] == 1} {
        puts [lindex $line 1] ;# Prints only 1
        puts $line            ;# Prints Whole Line which has 1 at end
    }
}

解决方案2:如果您想使用regexp,那么请使用group-capturing,即(.*)

set fd [open "somefile.txt" r]
while {[gets $fd line] >= 0} {
    if {[regexp "\{.*\} (.*)" $line match match1]} {
        if {$match1 == 1} {
            puts $line
        }
    }
}

解决方案3:基于@Peter关于regexp的建议。
set fd [open "somefile.txt" r]
while {[gets $fd line] >= 0} {
    if {[regexp {\d+$} $line match]} {
        if {$match == 1} {
            puts $match ;# Prints only 1
            puts $line  ;# Prints whole line which has 1 at end 
        }
    }
}

更好的正则表达式:使用regexp {\d+$} $line match,然后将 $match 与 1 进行比较。 - Peter Lewerin
@PeterLewerin,就我理解,您给出的正则表达式将找到所有末尾的数字。我理解正确吗? - toxic_boi_8041
是的,它会找到被搜索字符串末尾的最长连续数字字符串。 - Peter Lewerin
参考Axt_8041的解决方案1。 - ShivankG

0

你似乎需要捕获第一行和第三行末尾的数字。

以下是实现此目的的方法:

set s {{ abc1 } 1
{ cde1 } 101
{ fgh1 } 1
{ ijk1 } 2}
set re {^{[^{}]*}\s*(\d+)\s+{[^{}]*}\s*\d+\s+{[^{}]*}\s*(\d+)}
regexp $re $s m g1 g2
set res [expr $g1 + $g2]
puts $res

请查看IDEONE演示

模式匹配:

  • ^ - 字符串的开头
  • {[^{}]*} - 一个类似于{...}的字符串,其中没有括号
  • \s* - 0个或多个空格
  • (\d+) - 第1组(g1)捕获1个或多个数字
  • \s+ - 1个或多个空格(如果前后没有尾随/前导空格,则可以用[\r\n]+替换)
  • {[^{}]*}\s*\d+\s+{[^{}]*}\s*(\d+) - 参见上文,只是(\d+)将创建第二个变量g2

请查看正则表达式演示


0
如果您不使用正则表达式,这样的问题就会更容易解决一个数量级。
package require fileutil

::fileutil::foreachLine line somefile.txt {
    if {[lindex $line end] == 1} {
        puts $line
    }
}

这个解决方案查看文件中的每一行,并检查最后一个项目是否等于1。如果是,则打印该行。

您还可以对它们进行计数/求和:

set count 0
set sum 0
::fileutil::foreachLine line somefile.txt {
    if {[lindex $line end] == 1} {
        puts $line
        incr count
        incr sum [lindex $line end] ;# yeah, I know, always 1
    }
}
puts "Number of lines: $count"
puts "Sum of items: $sum"

如果您的Tcl安装中没有可用的fileutil,或者您不能或不想安装它,您可以使用更低级别的核心等效功能:
set f [open somefile.txt]
while {[gets $f line] >= 0} {
    if {[lindex $line end] == 1} {
        puts $line
    }
}
close $f

如果您非常需要使用正则表达式,在这种情况下,您可以这样做:
::fileutil::foreachLine line somefile.txt {
    if {[regexp {\m1$} $line]} {
        puts $line
    }
}

这个正则表达式可以找到以数字1结尾的单词(即在它前面没有数字或单词字符)。

文档:closefileutil 包,getsiflindexopenpackageputsTcl 正则表达式语法regexpwhile


是的,我想打印那些以1结尾的行(仅限1,不包括111、101或100)。我不能使用括号中的值,因为有数百万行,每行括号内的名称都不同。但是有一些行以1结尾,我只想打印这些行。 - ShivankG
我认为你的代码会起作用,但是我无法使用fileutil,因为我正在Linux机器上使用tcl。谢谢。 - ShivankG
@ShivankG:fileutil 在所有平台上都可用,但可能需要安装。 - Peter Lewerin
你可以在Linux机器上使用fileutil。如果你正在使用tclsh 8.5,那么它是内置的。 - toxic_boi_8041

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接