如何使用Tcl正则表达式提取所有匹配项?

7

大家好,我想要一个正则表达式的解决方案,我的问题是提取所有形式为H'xxxx的十六进制数,我使用了这个正则表达式,但我只得到了一个数字,如何从这个字符串中获取整个十六进制数。

set hex "V5CCH,IA=H'22EF&H'2354&H'4BD4&H'4C4B&H'4D52&H'4DC9"
set res [regexp -all {H'([0-9A-Z]+)&} $hex match hexValues]
puts "$res H$hexValues"

我的输出结果是5 H4D52。


单引号需要转义吗,我在想呢?:H'([0-9A-Z]+)& - Zabba
2
如果你正在处理十六进制数字,[0-9A-F] 应该就足够了。 - relet
@Zabba,在正则表达式中,单引号没有特殊含义,甚至在Tcl中也是如此。 - glenn jackman
@relet,这也可以:[[:xdigit:]] -- http://tcl.tk/man/tcl8.5/TclCmd/re_syntax.htm#M31 - glenn jackman
2个回答

29

关于 -all -inline

根据文档:

-all : 将正则表达式在字符串中尽可能多地匹配,返回找到的总匹配次数。如果与匹配变量一起使用,它们仅包含最后一次匹配的信息

-inline : 在使用-inline时,命令将返回数据列表,该列表通常会放置在匹配变量中。如果指定了-inline,则不能指定匹配变量。如果与-all一起使用,则在每次迭代中连接列表,以便始终返回扁平列表。对于每个匹配迭代,命令将附加整个匹配数据以及正则表达式中每个子表达式的一个元素。

因此,要在Tcl中将所有匹配(包括组捕获)作为扁平列表返回,可以编写以下代码:

set matchTuples [regexp -all -inline $pattern $text]
如果模式具有0...N-1个组,那么每个匹配都是列表中的N元组。因此实际匹配数量是该列表长度除以N。您可以使用foreachN个变量迭代列表中的每个元组。 例如,如果N=2,则您将拥有:
set numMatches [expr {[llength $matchTuples] / 2}]

foreach {group0 group1} $matchTuples {
   ...
}

参考资料


示例代码

以下是针对此特定问题的解决方案,带有输出注释(也可在ideone.com上查看):

set text "V5CCH,IA=H'22EF&H'2354&H'4BD4&H'4C4B&H'4D52&H'4DC9"
set pattern {H'([0-9A-F]{4})}
 
set matchTuples [regexp -all -inline $pattern $text]
 
puts $matchTuples
# H'22EF 22EF H'2354 2354 H'4BD4 4BD4 H'4C4B 4C4B H'4D52 4D52 H'4DC9 4DC9
# \_________/ \_________/ \_________/ \_________/ \_________/ \_________/
#  1st match   2nd match   3rd match   4th match   5th match   6th match
 
puts [llength $matchTuples]
# 12
 
set numMatches [expr {[llength $matchTuples] / 2}]
puts $numMatches
# 6
 
foreach {whole hex} $matchTuples {
   puts $hex
}
# 22EF
# 2354
# 4BD4
# 4C4B
# 4D52
# 4DC9

关于正则表达式

请注意,我稍微改了一下正则表达式的模式:

  • 而不是使用[0-9A-Z]+,例如使用[0-9A-F]{4}可以更具体地匹配恰好4个十六进制数字
  • 如果您坚持要匹配&,那么最后一个十六进制字符串(即您输入中的H'4DC9)将无法匹配。
    • 这就解释了为什么在原始脚本中你得到的是4D52,因为那是与&最后一次匹配的结果。
    • 也许去掉&,或者使用(&|$),即&或字符串结尾$

参考资料


谢谢,我不知道在那种情况下十六进制的数量如何评估? - Mallikarjunarao Kosuri
@polygen 我尝试使用数组操作,但是数组大小只显示了一次,最后一个元素存储在其中,请提供一个示例。 - Mallikarjunarao Kosuri
1
@polygene:我建议你将这个编辑到你的答案中,因为这是惯用的方法。 - Donal Fellows
2
@polygene: +1:看起来非常不错。如果你要写更多的Tcl,请记得在表达式周围加上大括号(除非你真的知道自己在做什么),因为这样可以让它们被编译,并避免类似于SQL注入攻击的问题;大括号表达式是无风险和快速的。 - Donal Fellows
@Donal fellows 如何在正则表达式中使用-about选项 - Mallikarjunarao Kosuri
@Malli:regexp -about $RE 返回一个两项列表,描述 RE 的特性。其中一项是捕获组的数量,另一项是特性列表(实际上是 RE 内部 flags 字段位的转储)。如果您想要更多信息,请适当地提出问题! - Donal Fellows

2

我不是Tcl专家,但我认为你需要同时使用-inline-all选项:

regexp -all -inline {H'([0-9A-Z]+)&} $string

编辑:这里再次提供内容,这次使用了更正的正则表达式(请查看评论):

regexp -all -inline {H'[0-9A-F]+&} $string

但是,使用 -inline 输出可以类似于正则表达式 -all {H'(0-9A-Z]+)&} $string 匹配。puts $match。我们还可以写成这样。上面的正则表达式会生成 H'22EF& 22EF H'2354& 2354 H'4BD4& 4BD4 H'4C4B& 4C4B H'4D52& 4D52。我不需要这个输出,我只需要十六进制值。 - Mallikarjunarao Kosuri
2
我只是在演示 -all -inline 的使用,但 @poly 是对的:你需要摆脱那些括号。它们不需要用于分组,并且它们会向结果数组添加许多不需要的子字符串。 - Alan Moore

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