POSIX正则表达式限制重复

时间:2011-04-17 22:00:35

标签: regex shell unix scripting

我正在尝试grep输入字符串允许的最大重复次数,但似乎无法使其正常工作。

输入文件有三行,分别为3,5和7次重复的“pq”。 > = 3,> = 5表达式工作正常,但“3到5之间”表达式{3,5}也表示该行有七次重复。

DEV /> cat input.txt
pq -- One occurance of pq
pqpqpqpqpq -five occurances of pq
pqpqpqpqpqpqpq -- seven occurances of pq


DEV /> grep "\(pq\)\{3,\}" input.txt
pqpqpqpqpq -five occurances of pq
pqpqpqpqpqpqpq -- seven occurances of pq


DEV /> grep "\(pq\)\{5\}" input.txt
pqpqpqpqpq -five occurances of pq
pqpqpqpqpqpqpq -- seven occurances of pq

DEV /> grep "\(pq\)\{3,5\}" input.txt
pqpqpqpqpq -five occurances of pq
pqpqpqpqpqpqpq -- seven occurances of pq

我做错了什么或这是预期的行为?

如果这是预期的行为(因为包含7个PQ的字符串在3-5个PQ之间),

1)在什么情况下最大重复适用? {3,5}和{3,}(大于3)之间有什么区别?

2)我可以用“^”锚定我的正则表达式,但是如果我的字符串不以“pq”结尾并且有更多文本会怎么样?

2 个答案:

答案 0 :(得分:2)

如果一条线有七次重复的任何东西,那么它也包含3-5次重复的东西,并且在几个点上,不少于此。

如果您希望锚定匹配,请使用匹配锚点。否则,当然,他们不是。

/X{3,}//X{3,5}/之间的实际区别在于它匹配的字符串有多长 - 匹配的范围(或范围)。如果您正在寻找的是一个布尔的是/否响应,并且您的模式中没有任何其他内容,那么它并没有太大的区别;实际上,如果一个聪明的正则表达式引擎知道这样做是安全的,它将会提前返回。

查看区别的一种方法是使用GNU grep的 -o - only-matching 选项。观看:

$ echo 123456789 | egrep -o '[0-9]{3}'
123
456
789
$ echo 123456789 | egrep -o '[0-9]{3,}'
123456789
$ echo 123456789 | egrep -o '[0-9]{3,5}'
12345
6789
$ echo 123456789 | egrep -o '[0-9]{3,5}[2468]'
123456
$ echo 123456790 | egrep -o '[0-9]{3,5}[13579]'
12345
6789

要了解最后两个如何工作,了解正则表达式引擎的尝试(包括回溯步骤)非常有用。你可以用这种方式使用Perl来做到这一点:

$ perl -Mre=debug -le 'print $& while 1234567890 =~ /\d{3,5}[13579]/g'
Compiling REx "\d{3,5}[13579]"
Final program:
   1: CURLY {3,5} (4)
   3:   DIGIT (0)
   4: ANYOF[13579][] (15)
  15: END (0)
stclass DIGIT minlen 4 
Matching REx "\d{3,5}[13579]" against "1234567890"
Matching stclass DIGIT against "1234567" (7 chars)
   0 <> <1234567890>         |  1:CURLY {3,5}(4)
                                  DIGIT can match 5 times out of 5...
   5 <12345> <67890>         |  4:  ANYOF[13579][](15)
                                    failed...
   4 <1234> <567890>         |  4:  ANYOF[13579][](15)
   5 <12345> <67890>         | 15:  END(0)
Match successful!
12345
Matching REx "\d{3,5}[13579]" against "67890"
Matching stclass DIGIT against "67" (2 chars)
   5 <12345> <67890>         |  1:CURLY {3,5}(4)
                                  DIGIT can match 5 times out of 5...
  10 <1234567890> <>         |  4:  ANYOF[13579][](15)
                                    failed...
   9 <123456789> <0>         |  4:  ANYOF[13579][](15)
                                    failed...
   8 <12345678> <90>         |  4:  ANYOF[13579][](15)
   9 <123456789> <0>         | 15:  END(0)
Match successful!
6789
Freeing REx: "\d{3,5}[13579]"

如果您对比赛后的内容有其他限制,那么您选择的重复类型可以产生很大的不同。在这里,我将对允许每个匹配完成的位置施加约束,说它需要在奇数位之前结束:

$ perl -le 'print $& while 1234567890 =~ /\d{3}(?=[13579])/g'
234
678
$ perl -le 'print $& while 1234567890 =~ /\d{3,5}(?=[13579])/g'
1234
5678
% perl -le 'print $& while 1234567890 =~ /\d{3,}(?=[13579])/g'
12345678

因此,当你有事情必须在事后发生时,它可以产生很大的不同。当你只是决定整行是否匹配时,它可能不那么重要。

答案 1 :(得分:1)

这是预期的行为。字符串“pqpqpqpqpqpqpq”实际上确实有三到五次重复的“pq”,然后还有一些更好的措施。您可能希望尝试锚定正则表达式,例如^\(pq\)\{3,5\}$


编辑以匹配已修改的问题:

  1. 最大值适用于所有情况。发生的事情是,grep匹配7个重复的“pq”中的5个(很可能是前5个),并且因为它找到了匹配,所以它打印出该行。
  2. 你必须找到一种方法来改变你的正则表达式,以匹配你想要的,而不是你不想要的。例如,要匹配以3-5次重复“pq”开头的行,您可能会执行以下操作:^\(pq\){3,5}\($|[^p]|p$|p[^q]\)。这匹配3-5“pq”,紧接着是行尾或任何字符 - 除了“p”或“p” - 后续行或“p” - 后续行为 - 通过任何字符的-另一不包括─ “q”。