【问题标题】:ERE - adding quantifier to group with inner group and back-referenceERE - 将量词添加到具有内部组和反向引用的组
【发布时间】:2017-04-23 15:19:46
【问题描述】:

试图获取连续重复字母出现两次或三次的单词。无法找到使用 ERE 使用量词和捕获组的方法

$ grep --version | head -n1
grep (GNU grep) 2.25

$ # consecutive repeated letters occurring twice
$ grep -m5 -xiE '[a-z]*([a-z])\1[a-z]*[a-z]*([a-z])\2[a-z]*' /usr/share/dict/words
Abbott
Annabelle
Annette
Appaloosa
Appleseed

$ # no output for this, why?
$ grep -m5 -xiE '([a-z]*([a-z])\2[a-z]*){2}' /usr/share/dict/words


虽然适用于-P

$ grep -m5 -xiP '([a-z]*([a-z])\2[a-z]*){2}' /usr/share/dict/words
Abbott
Annabelle
Annette
Appaloosa
Appleseed

$ grep -m5 -xiP '([a-z]*([a-z])\2[a-z]*){3}' /usr/share/dict/words
Chattahoochee
McConnell
Mississippi
Mississippian
Mississippians


感谢 Casimir et Hippolyte 提供更简单的输入和正则表达式来测试此行为

$ echo 'aazbb' | grep -E '(([a-z])\2[a-z]*){2}' || echo 'No match'
aazbb
$ echo 'aazbbycc' | grep -E '(([a-z])\2[a-z]*){2}([a-z])\3[a-z]*' || echo 'No match'
aazbbycc
$ echo 'aazbbycc' | grep -P '(([a-z])\2[a-z]*){3}' || echo 'No match'
aazbbycc

$ # failing case
$ echo 'aazbbycc' | grep -E '(([a-z])\2[a-z]*){3}' || echo 'No match'
No match

sed 的行为也相同

$ sed --version | head -n1
sed (GNU sed) 4.2.2

$ echo 'aazbb' | sed -E '/(([a-z])\2[a-z]*){2}/! s/.*/No match/'
aazbb    
$ echo 'aazbbycc' | sed -E '/(([a-z])\2[a-z]*){2}([a-z])\3[a-z]*/! s/.*/No match/'
aazbbycc

$ # failing case
$ echo 'aazbbycc' | sed -E '/(([a-z])\2[a-z]*){3}/! s/.*/No match/'
No match


相关搜索链接,我查了一些,但没有得到任何接近这个问题的东西

如果这在grepsed 的较新版本中得到解决,请告诉我。此外,如果问题出现在非 GNU 实现中

【问题讨论】:

  • 还要注意:echo 'aazbb' | grep -m5 -xiE '(([a-z])\2[a-z]*){2} 有效,echo 'aazbbycc' | grep -m5 -xiE '(([a-z])\2[a-z]*){3} 无效。我怀疑 grep 会默默地中止复杂度过高的模式。
  • @CasimiretHippolyte 似乎是这样,感谢您的输入。我今天将尝试在这些行上进行更多搜索:)
  • 关于您在 Ed Morton 回答中的评论,BRE 和 ERE 模式下的 grep 以完全不同的方式工作(与使用 -P 的方式不同),它不使用回溯机制(简而言之,所有可能的路径都被存储和最长的胜利)。
  • 请注意,gnu 3 文档说反向引用存在问题,并且可能由于堆栈溢出而悄然死亡。递归限制也可能是 2。
  • @Sundeep - 是的,我想就是这样。但是,关于人为限制。很多时间作者会将这个限制设置为默认值,而不是等待那个指数时间来找出答案。您的第 2 组构造很简单,但是它们通常不会区分,它可能很复杂。我认为问题在于嵌套组构造中的反向引用。您可以配置全局 grep 环境参数来更改这些参数,即。堆栈大小,递归限制等。但是我不确定。

标签: regex sed grep gnu pcre


【解决方案1】:

我想-E 不允许Quantifiers,这就是为什么它只适用于-P


匹配 2 个或多个连续的重复字母组:

grep -P '(?:([a-z])\1*([a-z])\2){1}' /usr/share/dict/words

匹配3个或更多连续的重复字母组:

grep -P '(?:([a-z])\1*([a-z])\2){2}' /usr/share/dict/words

选项:

-P, --perl-regexp         PATTERN is a Perl regular expression

【讨论】:

  • -E 切换到 ERE(扩展正则表达式),可用的量词有:?*+{n}{m,n}
  • ERE 确实允许量词...echo 'ac abc abbc abbbc' | grep -Eo 'ab{1,2}c',也允许使用组...grep -xE '([a-d][r-z]){3}' /usr/share/dict/words
  • 我不确定,这就是我使用 “我想” 的原因,我会更新我的答案。谢了。
  • @PedroLobito 感谢您的意见。我对为什么 ERE 版本不工作感兴趣,编辑问题以添加更多示例和信息。
【解决方案2】:

更新

四处搜索后,我在我的 Windows 机器上安装了 gnugrep32,然后运行
一些测试:

我是从一篇旧的 SO 帖子中读到的:

非贪婪匹配不是 grep 支持的扩展正则表达式语法的一部分

因此,我们使用[a-z]{0,20} 作为测试,而不是[a-z]*[a-z]*?,其中? 被忽略(wtf?)

以下是使用整体 (){n} 的增量测试,以了解它在它之前会走多远停止回溯
成帧。


上班时间

(([a-z])\2[a-z]{0,20}){1}   len = 2    rr
(([a-z])\2[a-z]{0,20}){2}   len = 4    rrrr
(([a-z])\2[a-z]{0,20}){3}   len = 25   rrrrrrrrrrrrrrrrrrrrrrrrr
(([a-z])\2[a-z]{0,20}){4}   len = 47   rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr
(([a-z])\2[a-z]{0,20}){5}   len = 69   rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr
(([a-z])\2[a-z]{0,20}){6}   len = 91   rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr

{3}{6} 增量长度等于22。

这恰好是捕获帧表达式的全长([a-z])\2[a-z]{0,20}
当它回溯到之前的帧时。

结论是它会在 2 帧后自动停止回溯。

这是有道理的,例如,在 20 帧中,它达到 16 帧,并发现它无法匹配。
如果它回到第 1 帧并在那里调整并重新尝试一遍。

为什么应该这样做。
不过现在已经消耗了这么多内存,肥猪只好解开这一切了。
使用这个古老的实用工具可能需要很长时间。
嘿,最好将其限制为 2 帧。

当然,(([a-z])\2[a-z]*){3} 没有测试用例,因为贪心量词 *
如果它们都是 [a-z] 并且永远不会消耗第二帧上的整条线
开始第三帧。

【讨论】:

  • @Sundeep - ERE 代表什么?也许您没有看到我的回答的这一部分It didn't work with (([a-z])\2[a-z]*){3},但请继续投反对票,我不在乎。
  • 我没有投反对票... ERE 是扩展正则表达式,您可以查看this manual 获取文档...所以,如果您使用echo 'aazbbycc' | grep -E '(?:([a-z])\1[a-z]*){2}',您会在 GNU grep 上出现语法错误
  • 嘿,你能解释一下吗?你是如何测试这个的,你所说的框架是什么意思?看起来你已经找到了一种方法来了解引擎内发生的事情......另外,Casimir mentioned in comments grep doesn't use the backtracking mechanism (in short all possible paths are stored and the longest wins)
  • @Sundeep - 这完全没有意义。回声“aabbXcc”| grep -E "(([a-z])\2.{0,3}){3}" no match echo "aabbXXcc" | grep -E "(([a-z])\2.{0,3}){3}" "aabbXXcc"
【解决方案3】:
$ # no output for this, why?
$ grep -m5 -xiE '([a-z]*([a-z])\2[a-z]*){2}' /usr/share/dict/words

因为您搜索的是内部(至少)双字母的双组(两次相同)。像abbcabbc [(...) = "abbc" 2 次] 之类的东西,而不是 2 个(最终相似的)组,其中每个组都有一个双字母 likeabbcdeef

有 2 个后参考:

$ grep -iE '[a-z]*([a-z])\1{1,}[a-z]*([a-z])\2{1,}[a-z]*`

【讨论】:

  • 我无法理解您的答案...([a-z]*([a-z])\2[a-z]*){2} 旨在匹配AbbottAnnette 等词...不仅是abbcabbc...我不知道不知道如何在不使用反向引用的情况下做到这一点...[a-z]{2,} 表示任何字母 2 次或更多次...这将匹配 abxyz 也.. 不限于像 ee 这样的重复字母或oo
  • 你是对的,对不起。后向引用是强制性的(在另一个项目上忙于使用相同类型的问题 vbut 使用变量 [所以总是相同的内容]。我首先删除(非后向引用的正则表达式)
  • no probs.... 扩展版本将是[a-z]*([a-z])\1[a-z]*[a-z]*([a-z])\2[a-z]* 问题中已经提到...或[a-z]*([a-z])\1[a-z]*([a-z])\2[a-z]* 删除中间的冗余字符类...对于3个这样的重复对, [a-z]*([a-z])\1[a-z]*([a-z])\2[a-z]*([a-z])\3[a-z]* 等等......我的问题是为什么 ([a-z]*([a-z])\2[a-z]*){2}([a-z]*([a-z])\2[a-z]*){3} 不起作用......因为这样写起来更紧凑更清晰
  • 可能我的措辞不清楚..但我不是在搜索([a-z])\1{1,} ...eeoo就足够了,不需要eee或@987654344 @等...Abbott包含两个重复的字母bbtt...和Chattahoochee包含三对...ttooee
  • .. 我的问题是为什么看到回复的开头。主要是因为您搜索了两次相同的 whole 模式而不是 2 组,其中每个组都包含一个双字母。 (与我的第一个回复完全相反的错误)
【解决方案4】:

我提交了一个问题https://debbugs.gnu.org/cgi/bugreport.cgi?bug=26864,该手册现已更新以反映此类问题。

来自https://www.gnu.org/software/grep/manual/grep.html#Known-Bugs

反向引用会大大减慢匹配速度,因为它们会产生成倍增加的匹配可能性,这会消耗时间和内存来探索。此外,反向引用的 POSIX 规范有时也不清楚。此外,许多正则表达式实现存在反向引用错误,可能导致程序返回错误答案甚至崩溃,而修复这些错误通常是低优先级的:例如,截至 2020 年,GNU C library bug database 包含反向引用错误@ 987654324@、10844110532426925322,几乎没有即将修复的迹象。幸运的是,反向引用很少有用,在实际应用中避免它们应该不成问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-11
    • 1970-01-01
    • 1970-01-01
    • 2021-09-23
    • 2017-06-08
    • 2015-02-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多