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

6
使用grep命令,我正在尝试匹配由两个字符组成的行,一个字符重复出现并紧跟着另一个字符,但仅当第一个字符出现的次数等于第二个字符出现的次数时才进行匹配。
例如,假设我只能匹配像'0'和'1'这样的两个字符。现在想象一下,如果有n个'0'字符,那么直接跟在后面的必须是n个'1'字符。例如:
''、'0011'、'000111'和'00000000001111111111'都将匹配。但是:'011'、'1100'和'110001'不会匹配。
我一直在使用捕获组并在perldoc中查找有关grep -P更多信息来解决我的问题,但没有找到任何线索-至少用grep命令如此。
如何使用grep命令根据这些限制匹配字符串?
编辑:
在此示例中,0应该在1之前,因为有“直接跟随”限制。
空字符串也应是匹配情况,因为按照示例限制,当有n个0时,应有n个1,因此零个0应该没有1。

3
我一直在尝试使用捕获组,并在 perldoc 中浏览有关 grep -P 的更多信息。很好,你提到了你尝试过许多东西,请在你的问题中添加它们以避免关闭投票,谢谢。 - RavinderSingh13
3
展示你已经做过和尝试过的事情可以帮助别人更好地帮助你。这非常有帮助,会产生很大的影响。 - zdim
3
你确定 1100 不能匹配吗?你所说的是无论第一个和第二个字符是什么,而不是一个0后面跟着相同数量的1。 - bruno
2
还有为什么空行必须匹配?空行不使用2个字符。请编辑您的问题以澄清。 - bruno
2
@Shawn 对我来说这一点都不清楚,问题的第一部分是一个陈述,但第二部分只是一个例子。空行匹配的事实也很奇怪,与其他部分不兼容。希望原帖作者能够澄清。 - bruno
显示剩余4条评论
4个回答

5

请参见下面的编辑以获取澄清更新


这里是一个Perl的一行代码,代替了grep

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

匹配的长度比较明显是在正则表达式之外进行的;我不认为可以很好地在正则表达式内部进行匹配,而且我认为使用非正则表达式的代码没有任何问题 :)

这不能与单个字符匹配 (ab),这似乎与问题无关。锚点 (^$) 只能匹配具有两个字符的字符串,这似乎是指定的。

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

这是必需的,因为 \g1 指的是第一个捕获,即最先开始的括号集 (最左侧),这是整个模式的捕获。(我们可以使用 \g2 但这是不好的做法。)

使用命名引用可能会更好,但也更加复杂。


编辑 根据澄清,首先必须是0,然后是相同数量的1,并且计算0的重复次数(因此为空行),当然还有1的重复(因此是01)。这大大简化了问题,只需

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

"

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

在问题中的示例输入上,如输入file

0011
000111 00000000001111111111 01
011 1100 110001

输出结果如下:

0011
000111 00000000001111111111 01

(输出结果中的最后一行为空行,即中间的空行之后再没有匹配的行)

"

也就是说,不使用在正则表达式内运行代码的复杂功能,这会使它变得更加复杂。如果你仍然想尝试,可以查看perlreperlretut

或者,也可以使用正则表达式中的递归来完成,具有类似(或稍微较少?)的复杂性。


是的,所有这些都很奇怪。我在原始问题中放了两个备注,希望能够得到澄清。 - bruno
1
@bruno 好的,感谢您这样做,我们可以希望他们澄清。他们可能确实意味着 0 必须在 1 之前,但是他们必须明确指定,因为这可能意味着正在寻找特定字符,或者有一些其他标准(较小的数字优先?)... 如果他们没有清楚地说明这一点,我必须假设这是示例中的错误(考虑到该示例中有一个空行,很容易假设)。 - zdim
@zdim 感谢你的回答,你对 Perl 功能的解释非常好。我以为在示例中说“紧随其后有 n 个‘1'字符”已经足够表明 1 必须在 0 之后出现了。显然这并不清楚,我已经编辑了问题以澄清。 - Matthew Brian
这似乎也适用于像1111和00000000这样的情况。 - Matthew Brian
@MatthewBrian 更新了;现在使用固定字符非常简单,这些字符本身可以提供给“程序”(即使它全部在命令行上也是一个程序)。不过它仍然是一个Perl程序,不能被转换为grep的PCRE正则表达式,因为它使用语言来比较匹配的长度。 - zdim
显示剩余3条评论

3

只有你提供的这些样本,如果你可以使用 awk ,你可以尝试以下操作。

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

说明: 对上述内容进行详细解释。

awk '                          ##Starting awk program from here.
match($0,/^0+/){               ##Using match function to match starting zeroes here.
  num1=RLENGTH                 ##Creating num1 here with rlength.
  match($0,/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.

1
我之前没有看到,但警告空行必须匹配..如果不匹配,则会出现其他错误。 - bruno
2
@bruno,感谢您的提醒,您的意思是我应该添加匹配空行的条件吗?这在awk中很容易实现 :) 如果您确认我理解正确,我现在就会编辑我的答案。谢谢! - RavinderSingh13
2
只有楼主能够确认/澄清,希望楼主能够。 - bruno
我希望在我的第一次编辑中澄清了这一点。 - Matthew Brian

3

这个 awk 命令可以完成此操作:

cat file

0011

000111
00000000001111111111
011
1100
11000

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

0011
000111
00000000001111111111

如果你想打印可能会有后面跟着01的数字,则可以使用以下代码:

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

0011
000111
00000000001111111111
1100

gsub函数返回替换次数。


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

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

0011
000111
00000000001111111111
1100

grep正则表达式演示


2
011110001肯定不匹配。 1100不匹配的事实可能是OP问题中的错误。除了空行,您返回预期答案的相反内容。 - bruno
2
感谢@bruno,我之前打印的是不匹配的内容,但现在已经反过来了。我不确定OP是否也想要后面跟着01,但我在我的答案中也包括了这个选项。 - anubhava
1
希望最近的编辑已经澄清了这个问题。 - Matthew Brian

1
使用真正的正则表达式无法完成此操作,但由于递归的存在,可以使用Perl正则表达式完成。
/
   ^ (?&BALANCED)?+ \z

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

Terser:

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

演示:

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

0011
000111
00000000001111111111
011
1100
110001
.

0011
000111
00000000001111111111

请查看将文件指定为Perl one-liner要处理的文件
PCRE同样支持递归。因此,您可以使用以下内容:
grep -P '^((?:0(?1)1)?+)$'

演示:

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

0011
000111
00000000001111111111
011
1100
110001
.

0011
000111
00000000001111111111

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接