GNU grep 中 \< 和 \> 正则表达式符号的含义

GNU grep 中 \< 和 \> 正则表达式符号的含义

以下正则表达式示例来自“掌握正则表达式”的第 22 页。

'\<([a-z]+) +\1\>'

我是正则表达式的新手,但我的印象是,尽管这本书没有明确说明这一点,\<但 和 the\>不是标准的正则表达式符号。从搜索来看,这些似乎是正grep则表达式语法的 GNU 扩展。可能它不是特定的grep——我不确定。

任何状况之下,不清楚这些符号的含义。这个问题的其余部分引用了不同的定义,或尝试定义。其中一些显然是错误的、难以理解的,或者至少是不完整的。如果有人能给我一个准确的定义,我将不胜感激。我想说最后引用的是https://www.grymoire.com/Unix/Regular.html#uh-9,是最有可能是正确的。但官方文档却并非如此。

书上说

您可以使用(可能看起来很奇怪)元序列\<\>[...]。您可以将它们视为 和 的基于单词的版本,^分别$匹配单词开头和结尾的位置。

然后后来

“单词的开头”只是字母数字字符序列开始的位置; “词尾”是这样一个序列结束的地方。下一页的图 1-2 显示了标记了这些位置的示例线。

图 1-2 很有帮助。

它也被“记录”在GNU findutils 手册 其中说:

‘\<’ matches the beginning of a word
‘\>’ matches the end of a word

也在GNU grep 手册它说:

'\<'
    Match the empty string at the beginning of word.
'\>'
    Match the empty string at the end of word.

我不知道这些描述是什么意思。因此,GNU 手册中的这两段摘录都没有帮助。

在最初写这个问题时,我没有足够仔细地阅读“掌握正则表达式”部分,没有看到图1-2,并认为这些符号意味着在“单词”之前或之后有一个空白字符”。我现在意识到这是错误的。然而,即使这本书的描述也是不正确/不完整的。

考虑这两个例子:

grep --color -E -i '\<([a-z]+) +\1\>' <<< 'wibble someword someword-something else wibble'

这里匹配“someword someword”。

grep --color -E -i '\<([a-z]+) +\1\>' <<< 'wibble someword someword_something else wibble'

这里没有任何匹配。

这本书没有解释这一点,因为它说“单词结尾”是字母数字序列结束的地方。 GNU 手册的摘录也没有。

中提供了可能的解释https://www.grymoire.com/Unix/Regular.html#uh-9(在随机搜索中找到)上面写着:

搜索单词并不像乍看起来那么简单。字符串“the”将匹配单词“other”。您可以在字母前后添加空格并使用以下正则表达式:“ the ”。但是,这与行首或行尾的单词不匹配。并且与单词后面有标点符号的情况不匹配。

“t”之前的字符必须是换行符,或者是除字母、数字或下划线之外的任何字符。 “e”后面的字符也必须是除数字、字母或下划线之外的字符,或者它可能是行结束符。

我不知道作者是否从这里得到这个,但假设这是真的,它可以解释我所看到的行为。但它看起来仍然相当随意。-不是标点符号。为什么它不能作为单词的一部分包含在内?或者,换句话来说,为什么连字符与单词的结尾匹配,而下划线则不然?事实上,连字符在自然语言中比下划线更常见。尽管下划线可能用于编程语言中。

如果这是正确的,那么 GNU 文档确实应该正确记录它。如果我明白了这一点,是否可以提交错误报告?

理想情况下,此功能是可定制的。但也许这个要求太多了。

这段来自 GNUgrep代码的摘录表明,来自的描述www.grymoire.com可能是正确的。相关代码init_word_char在“lib/regcomp.c”中的函数中,看起来像

general_case:
  for (; i < BITSET_WORDS; ++i)
    for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
      if (isalnum (ch) || ch == '_')
        dfa->word_char[i] |= (bitset_word_t) 1 << j;

这里重要的一行当然是文件的第 983 行:

if (isalnum (ch) || ch == '_')

即该字符是字母数字或下划线。

当然,我不明白这段代码的大部分含义。

答案1

您似乎假设了与正则表达式相关的“单词”的正式且精确的定义,而其含义实际上取决于实现。

事实上,在“正则表达式命名法”、“风味”段落中,您引用的书指出

即使两个程序都支持 ⌈\<···\>⌋,它们也可能在它们所做的事情上存在分歧,并且不认为这是一个单词”。

作为这个概念的可变性的一个例子,维基百科上的正则表达式页面[:word:]将非标准字符类定义为包含下划线,但与[:word:]符号关联的脚注链接到Emacs Lisp 手册,其中据说字符类匹配“任何具有单词语法的字符”,进一步链接到语法类表将下划线列出在“单词成分”中(将其列出在“符号成分”中 - 定义为“变量和命令名称中使用的额外字符以及单词成分”)。

从这一点来看,上述说法明显不准确

“单词的开头”只是一系列字母数字字符开始的位置”

可能被视为一种简化而不是定义。

“匹配单词开头”和“匹配单词开头位置”这样的表达听起来也不是很正式。涉及空字符串的版本虽然不太清楚,但更精确,因为空字符串是一个正式定义的概念1 .

几乎,

'\<'
    匹配单词开头的空字符串。

表示\<仅当字符串包含单词组成字符(根据 GNU grep 定义,[:alnum:]字符类中的一个或 a _)且前面不紧接单词组成字符时,才匹配该字符串。

然后,示例中的模式\<([a-z]+) +\1\>可以被解读为“一个或多个小写字母字符的序列(在您的语言环境中在“a”和“z”之间进行排序),其中第一个字符前面没有紧接着单词组成字符,后跟一个或多个空格,后跟之前的整个小写字母字符序列,最后一个字符后面没有单词组成字符。


1 在正则表达式上下文中,长度为零的字符串。是可以匹配的,确实每一个line 包含它,包括空行。成为连接运算的单位元,它可以在任何文字字符之前或之后匹配。例如,它是X*在 中匹配的内容,或者在 中匹配的grep 'oX*o' <<<foo空模式或在 中匹配的内容;以及中匹配的内容。''grep '' <<<''echo "" | grep '^$'\<grep '\<' <<<'a'

答案2

那部分来自 GNU grep 手册说:

\<匹配单词开头的空字符串。

\>匹配单词末尾的空字符串。

它们匹配“单词”的开头和结尾,因此\<bar匹配字符串foo bar,或仅匹配bar,但不匹配foobar。匹配被描述为匹配空字符串,因为当匹配 时 \<barfoo bar匹配只是bar,而不是eg <space>bar,并且\<不会向匹配的字符串添加任何字符(这与eg 相关grep -o)。

它们不是标准的。

\w匹配单词成分,它是 的同义词[_[:alnum:]]

这就是手册接下来所说的。注意小字。单词字符包括字母数字(无论在当前语言环境中意味着什么),和下划线。因此与您的第二个尝试寻找的内容实际上someword_something不匹配。someword\>grep

是的,这是因为在许多编程语言中,标识符名称中允许使用字母数字和下划线。连字符不是,它是减号运算符。

当然,在 C 和 Javascript 中,$它在标识符名称中也是有效的,并且标识符名称不能以数字开头,但你不可能拥有一切。

相关内容