我想编写一个正则表达式,匹配xa?b?c?,但不匹配x。实际上,'x'、'a'、'b'和'c'不是单个字符,它们是相对复杂的子表达式,因此我想避免像x(abc|ab|ac|bc|a|b|c)这样的代码。有没有一种简单的方法在正则表达式中匹配“至少包含a、b和c中的一个,并且按照顺序出现”,或者说我没有机会了?
我想编写一个正则表达式,匹配xa?b?c?,但不匹配x。实际上,'x'、'a'、'b'和'c'不是单个字符,它们是相对复杂的子表达式,因此我想避免像x(abc|ab|ac|bc|a|b|c)这样的代码。有没有一种简单的方法在正则表达式中匹配“至少包含a、b和c中的一个,并且按照顺序出现”,或者说我没有机会了?
这是最简短的版本:
(a)?(b)?(c)?(?(1)|(?(2)|(?(3)|(*FAIL))))
如果你需要将匹配结果保存到一个单独的组中,可以这样写:
((a)?(b)?(c)?)(?(2)|(?(3)|(?(4)|(*FAIL))))
但是如果 a
, b
或者 c
包含捕获组,那么这并不是非常健壮的。因此,改为编写以下代码:
(?<A>a)?(?<B>b)?(?<C>c)?(?(<A>)|(?(<B>)|(?(<C>)|(*FAIL))))
如果你需要整个匹配的一个组,那么可以写成:
(?<M>(?<A>a)?(?<B>b)?(?<C>c)?(?(<A>)|(?(<B>)|(?(<C>)|(*FAIL)))))
如果和我一样喜欢使用多个字母标识符,并且认为没有 /x
模式这样的东西是不合理的,那么请写成这样:
(?x)
(?<Whole_Match>
(?<Group_A> a) ?
(?<Group_B> b) ?
(?<Group_C> c) ?
(?(<Group_A>) # Succeed
| (?(<Group_B>) # Succeed
| (?(<Group_C>) # Succeed
| (*FAIL)
)
)
)
)
以下是完整的测试程序,以证明它们都可以正常工作:
#!/usr/bin/perl
use 5.010_000;
my @pats = (
qr/(a)?(b)?(c)?(?(1)|(?(2)|(?(3)|(*FAIL))))/,
qr/((a)?(b)?(c)?)(?(2)|(?(3)|(?(4)|(*FAIL))))/,
qr/(?<A>a)?(?<B>b)?(?<C>c)?(?(<A>)|(?(<B>)|(?(<C>)|(*FAIL))))/,
qr/(?<M>(?<A>a)?(?<B>b)?(?<C>c)?(?(<A>)|(?(<B>)|(?(<C>)|(*FAIL)))))/,
qr{
(?<Whole_Match>
(?<Group_A> a) ?
(?<Group_B> b) ?
(?<Group_C> c) ?
(?(<Group_A>) # Succeed
| (?(<Group_B>) # Succeed
| (?(<Group_C>) # Succeed
| (*FAIL)
)
)
)
)
}x,
);
for my $pat (@pats) {
say "\nTESTING $pat";
$_ = "i can match bad crabcatchers from 34 bc and call a cab";
while (/$pat/g) {
say "$`<$&>$'";
}
}
所有五个版本都产生了这个输出:
i <c>an match bad crabcatchers from 34 bc and call a cab
i c<a>n match bad crabcatchers from 34 bc and call a cab
i can m<a>tch bad crabcatchers from 34 bc and call a cab
i can mat<c>h bad crabcatchers from 34 bc and call a cab
i can match <b>ad crabcatchers from 34 bc and call a cab
i can match b<a>d crabcatchers from 34 bc and call a cab
i can match bad <c>rabcatchers from 34 bc and call a cab
i can match bad cr<abc>atchers from 34 bc and call a cab
i can match bad crabc<a>tchers from 34 bc and call a cab
i can match bad crabcat<c>hers from 34 bc and call a cab
i can match bad crabcatchers from 34 <bc> and call a cab
i can match bad crabcatchers from 34 bc <a>nd call a cab
i can match bad crabcatchers from 34 bc and <c>all a cab
i can match bad crabcatchers from 34 bc and c<a>ll a cab
i can match bad crabcatchers from 34 bc and call <a> cab
i can match bad crabcatchers from 34 bc and call a <c>ab
i can match bad crabcatchers from 34 bc and call a c<ab>
不错,对吧?
编辑:对于开头部分的x
,只需在a
部分的第一个可选捕获组之前的第一个匹配项之前放置您想要的任何x
,就像这样:
x(a)?(b)?(c)?(?(1)|(?(2)|(?(3)|(*FAIL))))
或者像这样
(?x) # enable non-insane mode
(?<Whole_Match>
x # first match some leader string
# now match a, b, and c, in that order, and each optional
(?<Group_A> a ) ?
(?<Group_B> b ) ?
(?<Group_C> c ) ?
# now make sure we got at least one of a, b, or c
(?(<Group_A>) # SUCCEED!
| (?(<Group_B>) # SUCCEED!
| (?(<Group_C>) # SUCCEED!
| (*FAIL)
)
)
)
)
这个测试句子是没有 x
部分构建的,因此它对于该部分不起作用,但我认为我已经展示了我想如何完成这个任务。请注意,所有的 x
、a
、b
和 c
都可以是任意复杂的模式(是的,甚至是递归的),而且它们自己是否使用了编号分组也并不重要。
如果您想使用先行断言来处理这个问题,可以这样做:
(?x)
(?(DEFINE)
(?<Group_A> a)
(?<Group_B> b)
(?<Group_C> c)
)
x
(?= (?&Group_A)
| (?&Group_B)
| (?&Group_C)
)
(?&Group_A) ?
(?&Group_B) ?
(?&Group_C) ?
以下是在测试程序中添加到@pats
数组以展示此方法也有效的内容:
qr{
(?(DEFINE)
(?<Group_A> a)
(?<Group_B> b)
(?<Group_C> c)
)
(?= (?&Group_A)
| (?&Group_B)
| (?&Group_C)
)
(?&Group_A) ?
(?&Group_B) ?
(?&Group_C) ?
}x
请注意,即使使用了前瞻技术,我仍然设法不重复出现a
,b
或c
中的任何一个。
我赢了吗?☺
preg
。问题是我似乎无法推断出PHP支持哪个版本的PCRE!你有什么想法吗?在我的系统上,PCRE的pcre_version()
函数返回“8.10 2010-06-25”。我没有安装PHP,但也许只需要链接到一个正确构建的libpcre.so
。至少,“正确构建”的意思是指其pcre_config()
函数中的PCRE_CONFIG_UTF8 = 1
和PCRE_CONFIG_UNICODE_PROPERTIES = 1
。 - tchristregex
吗?这似乎是错误的,因为没有标签不应该能够独立存在。但是,你所说的听起来好像我们根本无法回答任何问题,因为我们甚至不知道基本的 BRE vs ERE 方言之间的区别。为什么人们不应该尽力而为呢?如果他们没有指定,我每次都会提供 Perl 解决方案。我认为我已经很清楚地说明了我也提供了 Perl 代码。 - tchrist如果你没有前瞻,这并不是一件轻松的事情。
x(ab?c?|bc?|c)
a
、b
或c
,而你的答案却重复了这些字母,因此似乎没有回答所提出的问题! - tchristx(?:a())?(?:b())?(?:c())?(\1|\2|\3)
a
、b
和c
之后的空捕获组,如果按照这个顺序匹配,将始终匹配(一个空字符串)。只有在前面的组参与匹配时,(\1|\2|\3)
部分才会匹配。因此,如果只有x
,则正则表达式失败。正则表达式的每个部分只会被评估一次。当然,如果x
、a
、b
和c
是包含自身捕获组的更复杂的子表达式,则必须相应地调整反向引用的编号*。由于这个正则表达式看起来有点奇怪,下面是详细版:x # Match x
(?:a())? # Try to match a. If this succeeds, \1 will contain an empty string.
(?:b())? # Same with b and \2.
(?:c())? # Same with c and \3.
(\1|\2|\3) # Now try to match the content of one of the backreferences.
# This works if one of the empty parentheses participated in the match.
# If so, the backref contains an empty string which always matches.
# Bingo!
^
和$
)来包围它,除非你不介意在字符串cxba
中匹配到xb
等内容。
例如,在Python中:
>>> r=re.compile(r"x(?:a())?(?:b())?(?:c())?(\1|\2|\3)$")
>>> for test in ("x", "xa", "xabc", "xba"):
... m = r.match(test)
... if m:
... print("{} --> {}".format(test, m.group(0)))
... else:
... print("{} --> no match".format(test))
...
x --> no match
xa --> xa
xabc --> xabc
xba --> no match
x(?:a(?P<a>))?(?:b(?P<b>))?(?:c(?P<c>))?((?P=a)|(?P=b)|(?P=c))
在Python/PCRE中,一个命名捕获组由`(?)`定义,例如`(?P...)`。 在.NET(可能还有其他变种)中,甚至可以有多个使用相同名称的捕获组,从而可以实现另一种简化:
x(?:a(?<m>))?(?:b(?<m>))?(?:c(?<m>))?\k<m>
Set(Value)?
应用于Set
,则反向引用1将为空(这是正确的),因此在正则表达式中再次引用它是可以的。但事实并非如此:Set(Value)?\1
无法匹配Set
。无论括号中是否包含内容,如果在正则表达式中使用了一个引用(引用正则表达式中根本没有参与匹配的部分),则整个正则表达式都会失败。 - Tim Pietzckerx
、a
、b
和/或c
中的嵌入式捕获组产生影响,从而扰乱编号方案吗?谢谢。 - tchrist\k<NAME>
、\k{NAME}
、\g{NAME}
和(?P=NAME)
;Perl支持\k<NAME>
、\k'NAME'
、\g{NAME}
和(?P=NAME)
。我个人喜欢\k<NAME>
,因为它与(?<NAME>...)
和(?(<NAME>)...|...)
很搭配。我不太喜欢冗长的Python版本。在Perl中匹配后,您可以通过$+{NAME}
访问最左边的名为NAME的捕获,通过$-{NAME}
访问所有名为NAME的捕获的数组引用。不确定Python如何处理多个捕获。您尝试过使用(?|...)
分支重置吗? - tchristx(?=[abc])a?b?c?
(a|b|c)
代替[abc]
。 - LarsHa
、b
或c
来解决这个问题。实际上,有几种不同的方法可以解决它。 - tchrist/(x)a?b?c?(?<!x)/;
x
是a
、b
或c
的终端子模式,它也不起作用。 - tchristx(ab?c?|bc?|c)
我相信它符合标准,同时最小化了重复(虽然还是有一些)。它也避免使用任何前瞻或其他处理器密集型表达式,这可能比节省正则表达式字符串长度更有价值。
这个版本重复了c
三次。你可以调整它,使得a
或b
是最常重复的一个,这样你就可以选择a
、b
和c
中最短的一个来重复三次。
b
和两次 c
,这正是 OP 说他想避免的。至少有三种解决方案可以避免这种重复,但你的解决方案不是其中之一。☹ - tchrist如果您不需要找到最大(贪婪)匹配,可以省略“按顺序”,因为如果您匹配x(a|b|c)
并忽略任何后续文本,则已经匹配了“至少一个a、b和c,按顺序”。换句话说,如果您只需要一个真/假答案(是否匹配),那么x(a|b|c)
就足够了。(另一个假设:您正在尝试确定输入字符串是否包含匹配项,而不是整个字符串是否与正则表达式匹配。即请参见@Alan Moore的问题。)
但是,如果您想要识别最大匹配或针对整个输入字符串进行匹配,则可以使用前瞻:x(?=(a|b|c))a?b?c?
这里有一些冗余,但比您试图避免的组合方法要少得多。