PHP正则表达式非捕获非匹配组

18

我正在制作一个日期匹配的正则表达式,目前进展顺利,我已经得到了如下的代码:

"/(?:[0-3])?[0-9]-(?:[0-1])?[0-9]-(?:20)[0-1][0-9]/"

这将(希望如此)匹配21世纪的单个或双位数日/月份和双位或四位数年份。经过一些尝试和错误,我已经做到了这一点。

但是,我对这些结果有两个简单的问题:

  1. (?: ) 这是什么意思?显然这是一个非匹配组,但是...

  2. 末尾的 ? 是什么意思?例如 (? )?

3个回答

38

[再次修改格式并修复介绍部分]

这是一则评论和答案。

答案部分... 我同意alex之前的答案。

  1. (?: )( )相反,用于避免捕获文本,通常是为了减少与所需内容混杂的反向引用或提高速度性能。

  2. 跟在(?: )后面的问号(或者除* + ?{}之外的任何符号)表示先前的项可能存在于一个合法匹配中,也可能不存在。例如,/z34?/将匹配z3和z34,但不会匹配z35或z等。

评论部分... 我对你正在处理的正则表达式进行了一些改进:

(?:^|\s)(0?[1-9]|[1-2][0-9]|30|31)-(0?[1-9]|10|11|12)-((?:20)?[0-9][0-9])(?:\s|$)
-- 首先,它避免了像0-0-2011这样的日期格式。
-- 其次,它避免了像233443-4-201154564这样的日期格式。
-- 第三,它包括了像1-1-2022这样的日期格式。
-- 第四,它包括了像1-1-11这样的日期格式。
-- 第五,它避免了像34-4-11这样的日期格式。
-- 第六,它允许您获取日期中的日、月和年,以便在代码中更轻松地引用。例如,可以编写进一步的检查代码(如第二组是否为2,并且第一组是否为29而且是闰年,或者第一组小于29),以确定一个2月29日是否合法。
最后请注意,仍然会有一些不存在的日期出现,例如31-6-11。如果要避免这种情况,请尝试:
(?:^|\s)(?:(?:(0?[1-9]|[1-2][0-9]|30|31)-(0?[13578]|10|12))|(?:(0?[1-9]|[1-2][0-9]|30)-(0?[469]|11))|(?:(0?[1-9]|[1-2][0-9])-(0?2)))-((?:20)?[0-9][0-9])(?:\s|$)
此外,我假设日期前后会有一个空格(或行首/行尾),但你可能需要调整它(例如,允许标点符号)。其他地方的评论者提到了这个资源,你可能会发现它很有用:http://rubular.com/

非常好,第一部分非常全面和有用。我目前正在使用/[0-3]?[0-9]-[0-1]?[0-9]-(?:20)?[0-1][0-9]/,然后在组件上运行checkdate()来处理虚假日期。 - Ben

8
  1. 这是一个非捕获组,您无法回溯引用它。通常用于减少回溯引用和/或提高性能。
  2. 它表示前面的捕获组是可选的。

Groovy。对于未来的用户,这里有一个半有用的进一步解释:http://www.regular-expressions.info/named.html - Ben

3

子模式

子模式由括号(圆括号)分隔,可以嵌套。将模式的一部分标记为子模式会做两件事:

  1. 它将一组备选项局部化。例如,模式cat(aract|erpillar|)匹配单词"cat"、"cataract"或"caterpillar"中的一个。如果没有括号,它将匹配"cataract"、"erpillar"或空字符串。
  2. 它将子模式设置为捕获子模式(如上所定义)。当整个模式匹配时,匹配子模式的主体字符串部分通过pcre_exec()函数的ovector参数传回给调用者。左括号从左到右计数(从1开始),以获取捕获子模式的编号。

例如,如果字符串"the red king"与模式the ((red|white) (king|queen))匹配,则捕获的子字符串是"red king"、"red"和"king",编号分别为1、2和3。

普通括号具有两个功能的事实并不总是有帮助的。通常需要分组子模式而不需要捕获要求。如果左括号后面跟着"?: ",则该子模式不执行任何捕获,并且在计算任何后续捕获子模式的编号时不计数。例如,如果字符串"the white queen"与模式the ((?:red|white) (king|queen))匹配,则捕获的子字符串为"white queen"和"queen",编号分别为1和2。最大捕获子字符串数量为65535。但是,根据libpcre的配置选项,可能无法编译这样大的模式。

作为一种方便的简写方式,如果在非捕获子模式的开头需要任何选项设置,则选项字母可以出现在"?"和":"之间。因此,以下两个模式:

(?i:saturday|sunday)
(?:(?i)saturday|sunday)

准确匹配相同的字符串集合。由于备选分支从左到右尝试,并且选项不会在子模式结束之前重置,因此一个分支中的选项设置会影响后续的分支,所以上述模式匹配"SUNDAY"以及"Saturday"。

可以使用语法(?Ppattern)来命名子模式。这个子模式将按其正常的数字位置和名称被索引到匹配数组中。PHP 5.2.2 引入了两种替代语法 (?pattern) 和 (?'name'pattern)。

有时需要在正则表达式中拥有多个匹配但交替的子组。通常,即使只有其中一个可能匹配,每个子组也会有自己的反向引用编号。为了解决这个问题,(?| 语法允许有重复的编号。考虑以下正则表达式与字符串Sunday匹配:

(?:(Sat)ur|(Sun))day

这里 Sun 存储在反向引用 2 中,而反向引用 1 是空的。匹配会将 Sat 存储在反向引用 1 中,而反向引用 2 不存在。将模式更改为使用 (?| 可以解决这个问题:

(?|(Sat)ur|(Sun))day

使用这个模式,太阳和土星都将存储在反向引用1中。
参考资料:http://php.net/manual/en/regexp.reference.subpatterns.php

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