TCL 中十六进制数的全局模式表达式?

Glob pattern expression for a hexadecimal number in TCL?

我正在尝试了解 glob 和正则表达式模式之间的区别。我需要在 TCL 中做一些模式匹配。

目的是查明是否输入了十六进制值。

  1. 该值可能以也可能不以 0x 开头
  2. 值应包含 1 到 12 个十六进制字符,即 0-9、a-f、A-F,如果 0x 存在,这些字符应跟在 0x 之后

问题是 glob 不允许使用 {a,b} 来说明要查找的字符数。另外,一开始我尝试使用 (0x[Xx])?但我认为这是行不通的。

使用glob不是必须的。我可以看到 glob 和正则表达式之间存在细微差别。我只想知道这是否只能通过正则表达式而不是 glob 来完成。

The thing is that glob does not allow use of {a,b} to tell about how many characters to look for. Also, at start I tried to use (0x[Xx])? but I think this is not working.

一个常用的正则表达式,完全不是 Tcl 特有的,是 ^(0[xX])?[A-Fa-f0-9]{1,12}$.

更新

正如 Donal 所写,在 regexp 方面存在 power-cost 权衡。我很好奇,并且对于给定的要求(可选 0x 前缀,范围检查 [1,12]),发现精心制作的脚本使用 string 操作,包括。 string match(请参阅下面的 isHex1)在此设置中优于 regexp(请参阅 isHex2),无论输入情况如何:

proc isHex1 {str min max} {
    set idx [string last "0x" $str]
    if {$idx > 0} {
      return 0
    } elseif {$idx == 0} {
      set str [string range $str 2 end]
    }
    set l [string length $str]
    expr {$l >= $min && $l <= $max && [string match -nocase [string repeat {[0-9a-f]} $l] $str]}
}

proc isHex2 {str min max} {
    set regex [format {^(0x)?[[:xdigit:]]{%d,%d}$} $min $max]
    regexp $regex $str
}

isHex1 扩展了根据输入长度(带或 w/o 前缀)和 string repeat 计算 string match 模式的想法。我自己的计时表明,在最坏的情况下(在范围内,最终字符决定),isHex1 运行速度至少比 isHex2 快 40%(全部使用 time,10000 次迭代)。其他情况(例如,out-of-range)要快得多。

string match 文档中描述了 glob 语法。与正则表达式相比,glob 是一个生硬的工具。

使用 regular expressions,您将获得标准字符 类,包括 [:xdigit:] 以匹配十六进制数字。

为了与 mrcalvin 的回答形成对比,Tcl-specific 正则表达式为:(?i)^0x[[:xdigit:]]{1,12}$

  • 前导(?i)表示表达式将匹配case-insensitively.

如果您只关心确定输入是否有效 数字,您可以使用 string is integer:

set s 0xdeadbeef
string is integer $s  ;# => 1

set s deadbeef
string is integer $s  ;# => 0

set s 0xdeadbeetle
string is integer $s  ;# => 0

Tcl 的 glob 模式比正则表达式 简单得多。他们只支持:

  1. *表示任意数量的任意字符。
  2. ?表示任何单个字符。
  3. […] 表示集合中的任何单个字符(括号内的字符,可能包括范围)。
  4. \x 表示文字 x (可以是任何字符)。这就是将 glob 元字符放入 glob 模式的方式。

它们也总是固定在两端。 (正则表达式更强大。它们也更慢。你为强大付出代价。)

要匹配像 0xF00d 这样的十六进制数字,您需要使用像这样的 glob 模式:

0x[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]

(或者,作为实际的 Tcl 命令;我们将模式放在 { 大括号 } 中以避免所有括号都需要大量反斜杠……)

string match {0x[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]} $value

请注意,我们必须匹配准确数量的字符。 (您可以使用 case-insensitive 匹配将模式缩短为 0x[0-9a-f][0-9a-f][0-9a-f][0-9a-f]。)

匹配十六进制数最好用 regexpscan (它也解析十六进制数)。每个人都喜欢忘记 scan 进行解析,但它很擅长......

regexp {^0x([[:xdigit:]]+)$} $value -> theHexDigits
scan $value "0x%x" theParsedValue