过度贪婪的正则表达式反向引用php preg_replace和一个不够贪婪的表达式

时间:2011-12-28 00:43:10

标签: php regex

我用谷歌搜索了regexinfo.com并且已经实现了几个小时,并且在我的生活中无法弄清楚这两个应该与meta标签匹配的正则表达式有什么问题。 任何帮助是极大的赞赏。 :)

Purp 1: 在preg_replace中使用$ 1时,捕获行的末尾的“>

'/<meta[\s]+[^>]*?name[\s]?=[\s"\']+keywords[\s"\']+content[\s]?=[\s"\']+([^"\']*)/ixU'

Purp 2: 不会随意捕捉线条,或多或少。 (别介意缺乏对''的支持)

'/<meta(?=[^>]*name="keywords")\s[^>$]*content="([^"]*)[">]*$/ixU

3 个答案:

答案 0 :(得分:0)

我发现你使用了三个PCRE modifiers­Docs

  1. i(PCRE_CASELESS) - 看起来不错,因为标签和属性名称在HTML中不区分大小写。
  2. x(PCRE_EXTENDED) - 您的模式看起来不需要这样。
  3. U(PCRE_UNGREEDY) - 不确定你是否真的需要它,可能更容易使用默认值并控制它自己的每次重复,例如仅在需要时使用特定量词来更改默认值。
  4. 您可能缺少的是m(PCRE_MULTILINE)修饰符,使$实际上与行尾相匹配。除非使用,$匹配主题字符串的结尾。

    一个解释正则表达式的好网站是http://www.regular-expressions.info/,如果我需要快速查找内容,我有时会查看,因为另一个good reference for PCRE都在一个文本文件中。

    对于您的情况,可能此页面对what is greedy and how to deal with it感兴趣。

答案 1 :(得分:0)

省略可选的空格并假设属性值周围只有双引号,你的第一个正则表达式就等于:

'/<meta\s+name="keywords"\s+content="([^"]*?)/i'

如果属性恰好按该顺序列出,则应该匹配content属性的开头引用之前的所有内容。在捕获组内部,[^"]*应该使用属性值,但由于您使用了U(ungreedy)标记,因此它最初不会消耗任何内容,就像[^"]*?一样。这就是正则表达式的结束,所以它报告了一个成功的匹配。

换句话说,你当前的问题是你遗漏了收盘价。如果您想匹配整个代码,则还需要添加结束>

'/<meta\s+name="keywords"\s+content="([^"]*)">/i'

但正如我所说,只有在只有两个属性的情况下它们才有效,并且它们按照该顺序列出,并且它不考虑单引号或不引用的属性值或可选的空格。

您的第二个正则表达式通过使用前瞻匹配name属性来处理排序问题。但它假设标签后面紧跟一个换行符,这不是你可以指望的东西。您应该使用结束>来标记匹配结束:

'/<meta\s+(?=[^>]*name="keywords")[^>]*content="([^"]*)"[^>]*>/i'

如果你想允许可选的空格:

'/<meta\s+(?=[^>]*name\s*=\s*"keywords")[^>]*content\s*=\s*"([^"]*)"[^>]*>/i'

我要强调的是,你的问题过度贪婪。这个正则表达式在没有U标志的情况下工作,除了正常的,贪婪的量词之外什么都没有。

答案 2 :(得分:0)

这个经过测试的功能应该做得很好:

// Fetch keywords from META element.
function getKeywords($text) {
    $re = '/# Match META tag having name=keywords values.
        <meta                 # Start of META tag.
        [^>]*?                # Lazily match up to NAME attrib.
        \bname\s*=\s*         # NAME attribute name.
        ["\']?keywords[\'"]?  # NAME attribute value.
        [^>]*?                # Lazily match up to CONTENT attrib.
        \bcontent\s*=\s*      # CONTENT attribute name.
        (?|                   # Branch reset group for keywords value.
          "([^"]*)"           # Either $1.1: a double quoted,
        | \'([^\']*)\'        # or  $1.2: single quoted value
        )                     # End branch reset group.
        [^>]*                 # Greedily match up to end of tag.
        >                     # Literal end of META tag.
        /ix';
    if (preg_match($re, $text, $matches)) {
        return $matches[1];
    } else {
        return 'No META tag with keywords.';
    }
}

请注意,延迟修饰符不是必需的,但会使它更快地匹配。

附加2011-12-28 OP澄清了表明只有一行文字可用的问题,因此可能会截断META标记的CONTENT属性值。这是一个不同的正则表达式,它捕获到捕获组1的CONTENT属性值(可能被截断)和标记的其余部分(如果它全部在一行上):

// Fetch keywords CONTENT attrib value from META element.
function getKeywords($text) {
    $re = '/# Match META tag having name=keywords values.
        <meta                 # Start of META tag.
        [^>]*?                # Lazily match up to NAME attrib.
        \bname\s*=\s*         # NAME attribute name.
        ["\']?keywords[\'"]?  # NAME attribute value.
        [^>]*?                # Lazily match up to CONTENT attrib.
        \bcontent\s*=\s*      # CONTENT attribute name.
        (?|                   # Branch reset group for keywords value.
          "([^"\r\n]*)"?      # Either $1.1: a double quoted,
        | \'([^\'\r\n]*)\'?   # or  $1.2: single quoted value
        )                     # End branch reset group.
        (?:                   # Grab remainder of tag (optional).
          [^>\r\n]*           # Greedily match up to end of tag.
          >                   # Literal end of META tag.
        )?                    # Grab remainder of tag (optional).
        /ix';
    if (preg_match($re, $text, $matches)) {
        return $matches[1];
    } else {
        return 'No META tag with keywords.';
    }
}