grep - 我如何匹配仅使用两个字符但每个字符出现相同次数的正则表达式?

grep - How would I match a regex using only two characters, but with each character occuring the same number of times?

我正在使用 grep 尝试匹配由两个字符组成的行,一个字符后跟重复字符,然后是另一个字符,但仅当第一个字符出现的次数等于第二个字符出现的次数时才匹配。

举个例子,假设我只能匹配两个字符,例如“0”和“1”。现在想象一下,如果有 n 个“0”字符,那么紧跟在后面的一定有 n 个“1”字符。例如:

都会匹配。但是:

不匹配。

我一直在玩弄捕获组,并通过 perldoc 搜索有关 grep -P 的更多信息,但没有找到任何线索来解决我的问题 - 至少使用 grep。

在给定这些约束的情况下,我如何创建 grep 命令来匹配字符串?

编辑:

仅使用您展示的示例,如果您对 awk 满意,您可以尝试以下操作。

awk 'match([=10=],/^0+/){num1=RLENGTH;match([=10=],/1+/);if(num1==RLENGTH){print}}' Input_file

说明: 为以上添加详细说明。

awk '                          ##Starting awk program from here.
match([=11=],/^0+/){               ##Using match function to match starting zeroes here.
  num1=RLENGTH                 ##Creating num1 here with rlength.
  match([=11=],/1+/)               ##Matching all ones now.
  if(num1==RLENGTH){ print }   ##Checking condition if num1 is equal to current length then print the line.
}
' Input_file                   ##mentioning Input_file name here.

请参阅下面的 EDIT 更新说明


这是 Perl 单行代码而不是 grep

perl -wne'print if /^((.)\g{-1}+)((.)\g{-1}+)$/ and length  == length ' file

匹配的长度比较显然是在正则表达式之外完成的;我看不出它可以很好地在内部完成,而且我看不出使用非正则表达式的代码有什么问题:)

这与单个字符 (ab) 不匹配,什么没有真正意义,什么似乎被排除在问题之外。锚点(^$)使得它只能匹配具有两个字符的字符串,这似乎是指定的。

那个\g{-1}是一个relative backreference。它匹配上次捕获的相同子模式,这是我们需要的,而不是简单的反向引用 (\g1)。

这是需要的,因为\g1指的是第一个捕获,parens组最先开始(最左边),这是对整个模式的捕获。 (我们 可以 使用 \g2 但将它们数掉是不好的做法。)

这可以通过使用命名引用变得更好,但这样也会更复杂。


EDIT 在澄清之后,它必须首先是 0s,然后是相同数量的 1s,并且 0-重复计数(所以空行),当然还有 1-重复(所以 01)。这大大简化了事情,因为

perl -wne'print if /^(0*)(1*)$/ and length  == length ' file

如果需要,01 可以作为外部参数提供的变量(所以它可以是任何语法,ab 等)。

它在问题的示例输入上按预期打印,因此在输入 file

0011

000111
00000000001111111111
01

011
1100
110001

它打印

0011

000111
00000000001111111111
01

(输出中最后一个空行是中间的空行,之后没有更多的行匹配)


也就是说,如果不使用 运行 在正则表达式中编码的棘手功能,这会使它变得更加复杂。如果您仍然想玩那个,请看 in perlrein perlretut.

或者,这也可以使用 recursion in regex 来完成,具有相似(或更小?)的复杂性。

awk 一行应该可以完成工作:

cat file

0011

000111
00000000001111111111
011
1100
11000
awk '/^0*1*$/ && gsub(/0/, "&") == gsub(/1/, "&")' file

0011
000111
00000000001111111111

或者如果你想打印可能有 1s 后跟 0s 的数字,那么使用:

# awk command
awk '/^(0*1*|1*0*)$/ && gsub(/0/, "&") == gsub(/1/, "&")' file

0011
000111
00000000001111111111
1100

gsub函数returns替换次数


由于您使用了 grep 标签,这里有一个 gnu grep 命令和 -P(PCRE 递归)正则表达式:

grep -P '^(0(?1)?1|1(?1)?0)?$' file

0011
000111
00000000001111111111
1100

grep RegEx Demo

使用真正的正则表达式不可能做到这一点,但由于递归的存在,使用 Perl 正则表达式可以做到这一点。

/
   ^ (?&BALANCED)?+ \z

   (?(DEFINE)
      (?<BALANCED> 0 (?&BALANCED)?+ 1 )
   )
/x

简述:

/^((?:0(?1)1)?+)\z/

演示:

$ perl -nle'print if /^((?:0(?1)1)?+)\z/' <<'.'

0011
000111
00000000001111111111
011
1100
110001
.

0011
000111
00000000001111111111

参见


PCRE 也支持递归。因此,您可以使用以下内容:

grep -P '^((?:0(?1)1)?+)$'

演示:

$ grep -P '^((?:0(?1)1)?+)$' <<'.'

0011
000111
00000000001111111111
011
1100
110001
.

0011
000111
00000000001111111111