【问题标题】:Regular Expression "Matching" vs "Capturing"正则表达式“匹配”与“捕获”
【发布时间】:2014-01-18 05:41:36
【问题描述】:

我一直在查找正则表达式教程以尝试掌握它们,并且一直在享受此链接中的教程,直到出现问题:http://regexone.com/lesson/12

我似乎无法弄清楚“匹配”和“捕获”之间的区别是什么。我写的任何内容似乎都没有选择“捕获”部分下的文本(甚至没有.*)。

编辑:下面是一个让我感到困惑的教程示例:(.* (.*)) 被认为是正确的,(.* .*) 不是。这是教程的问题还是我不​​理解的问题?

【问题讨论】:

  • 匹配是一个是否的问题,它匹配或不匹配。捕获返回表达式的一部分(括号中的部分)您可以捕获多个部分(可能是嵌套的)并返回一个列表
  • 我不确定返回在正则表达式中的含义,我只是理解匹配
  • 通过“捕获”,您可以告诉引擎匹配的哪些部分应该存储在某种寄存器中,以便您可以在表达式本身或替换值中使用值,具体取决于您正在使用表达式的函数。例如,([a-z])\1 将匹配任何重复的字母。 (...) 表示您要捕获此部分 natch 的值,\1 允许您访问第一个(并且仅在这种情况下)捕获的值。或者换句话说:\1 匹配第一个捕获组 ((...)) 匹配的任何内容。
  • 您最熟悉哪种编程语言?使用您熟悉的编程语言可以轻松形成一个清晰的示例。
  • 我熟悉几种语言,但我想我唯一一次在编程语言中看到正则表达式是在折旧的 PHP 中

标签: regex


【解决方案1】:

匹配:

当引擎匹配字符串的一部分或全部但确实返回 nothing

捕获:

当引擎匹配字符串的一部分或全部并返回 something

-- 返回是什么意思?

当您需要在需要捕获组之前检查/存储/验证/工作/喜欢您的正则表达式匹配的字符串的一部分时(...)

在您的示例中,此正则表达式 .*?\d+ 仅匹配日期和年份 See here

这个正则表达式.*?(\d+) 匹配整个并捕获年份See here

(.*?(\d+))会匹配整体,分别捕获整个和年份See here

*请注意标题为匹配组的右下角框

所以返回....

1:

preg_match("/.*?\d+/", "Jan 1987", $match);
print_r($match);

输出

Array
(
    [0] => Jan 1987
)

2:

preg_match("/(.*?\d+)/", "Jan 1987", $match);
print_r($match);

输出

Array
(
    [0] => Jan 1987
    [1] => Jan 1987
)

3:

preg_match("/(.*?(\d+))/", "Jan 1987", $match);
print_r($match);

输出

Array
(
    [0] => Jan 1987
    [1] => Jan 1987
    [2] => 1987
)

正如您在最后一个示例中看到的那样,我们有 2 个捕获组,在数组中索引为 12,以及 0始终是匹配的字符串,但不会被捕获。

【讨论】:

  • 为什么使用??是否使数字之前的字符串可选,以允许数字捕获组捕获所有数字,而不仅仅是第一个?这是我在使用 regex101.com 比较 (.*?(\d+))(.*(\d+) 之后得出的结论。我发现有趣的是,对于第一种情况:/.*?\d+/(仅捕获整个字符串),该问号不做任何事情,但被包括在内。
  • @BenButterworth 你可能会觉得这很有帮助stackoverflow.com/q/2301285/1020526
【解决方案2】:

在正则表达式中捕获意味着表明您不仅对匹配感兴趣(即查找与您的正则表达式匹配的字符串),而且您还对稍后使用匹配字符串的特定部分感兴趣。

例如,您链接到的教程的答案是(\w{3}\s+(\d+))

现在,为什么?

要简单地匹配日期字符串,写\w{3}\s+\d+(3 个单词字符,后跟一个或多个空格,后跟一个或多个数字)就足够了,但是在表达式中添加捕获组(捕获组是简单地括在括号()) 中的任何内容都将允许我稍后提取整个表达式(使用“$1”,因为最外面的一对括号是解析器遇到的第一个括号)或仅提取年份(使用“$2”,因为\d+ 周围的第二对括号是正则表达式解析器遇到的第二对括号)

当您不仅对匹配字符串与模式感兴趣,而且对从匹配的字符串中提取数据或以任何方式修改它们感兴趣时,捕获组会派上用场。例如,假设您想为教程中的每个日期添加 5 年 - 能够从匹配的字符串中仅提取年份部分(使用 $2)会派上用场

【讨论】:

  • 好的,谢谢。我假设$1$2 等是特定于 PHP 的。在这种情况下,您能指出我将用来生成这些变量的 PHP 函数的引用吗?您真的必须将它们作为单独的变量而不是数组来访问吗?
  • 捕获的概念对于所有正则表达式实现都是通用的。特别是在 php 中,访问捕获组的方法似乎是通过捕获索引访问 matches 数组。在此处查看 preg_match 的文档 - php.net/manual/en/function.preg-match.php
【解决方案3】:

简而言之,“捕获”将收集到的值保存在一个特殊的位置,以便您以后可以访问它。

正如一些人所指出的,捕获的内容可以在“稍后”以相同的模式使用,因此

/(ab*c):\1/

将匹配 ac:ac、abc:abc 或 abbc:abbc 等。 (ab*c) 将匹配 a、任意数量的 b,然后是 a c。无论它匹配什么都是“捕获”的。在许多编程和脚本语言中,\1、\2 等语法具有特殊含义,指的是第一个、第二个等捕获。由于第一个可能是 abbc,那么 \1 位必须仅匹配 abbc,因此唯一可能的完全匹配将是 'abbc:abbc'

Perl(我认为)PHP 都允许 \1 \2 语法,但它们也使用被认为更现代的 $1 $2 等。许多语言都从 Perl 中汲取了强大的 RegEx 引擎,因此世界上越来越多地使用它。

由于您的示例问题似乎在 PHP 网站上,因此在 PHP 中 $1 的典型用法是:

/(ab*c)(de*f)/

稍后(例如下一行代码)

$x = $1 . $2;   # I hope that's PHP syntax for concatenation!

因此,在您下次使用正则表达式之前,捕获一直可用。根据所使用的编程语言,这些捕获的值可能会被下一次模式匹配破坏,或者它们可能通过特殊语法或语言的使用永久可用。

【讨论】:

  • 谢谢,确实是PHP拼接
【解决方案4】:

看看这两个正则表达式 - 从你的例子中

# first
/(... (\d\d\d\d))/
#second
/... \d\d\d\d/

它们都匹配“Jun 1965”和“May 2000”
(顺便还有很多其他的东西,比如“555 1234”)

第二个匹配它 - 是的

你可以这么说

if ($x=~/... \d\d\d\d/){do something}

第一个捕获所以

/(... (\d\d\d\d))/
print $1,";;;",$2

将打印“Jun 1967;;;1967”

【讨论】:

  • 我刚刚意识到你的... 不是省略号,所以现在它更有意义了。 $1$2 呢?
  • @asimes $1$2 被称为“反向引用”。它们是保存捕获组内匹配的任何数据的特殊字符。基本语法是美元符号$,后跟一个等于捕获组顺序的数字。在上面的例子中,$1 持有 ... \d\d\d\d,因为它与最外面的一对括号对齐,而 $2 仅持有 \d\d\d\d,因为它与第二对括号对齐。
  • 捕获的字符串存储在$1$2$3等中,有多少有父级的表达式,从左边数左括号。
  • 当正则表达式本身引用匹配时,适当的“反向引用”会发挥作用。例如,(.).\1 将匹配 "aba" 和 "bbb" 但不匹配 "abb" 或 "abc"; \1 指回第一个带括号的表达式,这里是第一个字符;所以第三个字符必须与第一个字符相同。该组还将在$1 中捕获。
猜你喜欢
  • 1970-01-01
  • 2021-12-24
  • 2021-05-28
  • 2014-01-30
  • 2015-08-05
  • 1970-01-01
  • 2018-12-30
  • 2016-04-06
  • 1970-01-01
相关资源
最近更新 更多