【问题标题】:Repeating capture group with a regex pattern使用正则表达式模式重复捕获组
【发布时间】:2017-12-30 14:48:45
【问题描述】:

我正在尝试从网站上获取包含各个产品代码的产品列表。 产品代码为 5 位代码,元素的复杂程度从

<p>Part Number: 67001</p>

<p>Part Number: 50545 &ndash; 450g Cartridge 50525 - 2.5kg Tub 50520 - 20kg Pail 50555 - 55kg Drum 50575 *Indent - 175kg Drum</p>

不幸的是,5 位数的模式遍布整个网页,所以我不能只使用/\d{5}/

我正在使用一个正则表达式,它只提取零件编号元素中的 5 位数字,而不是从网页的其余部分中提取。

类似:/\&lt;p\&gt;Part\s*Number\:\s*((\d{5}) repeat this capture group n times)\&lt;\/p\&gt;/

我知道我可以通过分阶段分解页面并一个接一个地应用正则表达式来做到这一点。例如

第一阶段/\&lt;p\&gt;Part\s*Number\:\s*.*?\&lt;\/p\&gt;/
第二阶段/\d{5}/

但是是否有可能在一个正则表达式模式中做到这一点,如果可以,怎么做?

【问题讨论】:

    标签: php regex preg-match-all pcre digits


    【解决方案1】:

    我现在比一年前聪明得多,所以我完全放弃了我最初的建议。尝试解析有效​​ html 时最好/最可靠的方法是使用 dom 解析器。 XPath 使寻找节点/元素变得超级容易。一旦您取消了不包含 Part Number 关键字的 &lt;p&gt; 标记,正则表达式模式仍然是合适的工具。

    代码:(Demo)

    $html = <<<HTML
    <p>Zip Code: 99501</p>
    <p>Part Number: 67001</p>
    <p>Part Number: 98765 - 10000kg capacity</p>
    <p>Some dummy/interfering text. Part Number: 12345</p>
    <p>Zip Codes: 99501, 99524 , 85001 and 72201</p>
    <p>Part Number: 50545 &ndash; 450g Cartridge 50525 - 2.5kg Tub 50520 - 20kg Pail 50555 - 55kg Drum 50575 *Indent - 175kg Drum</p>
    HTML;
    
    $partnos = [];
    
    $dom = new DOMDocument;
    $dom->loadHTML($html);
    $xpath = new DOMXPath($dom);
    foreach ($xpath->query("//p[starts-with(., 'Part Number: ')]") as $node) {
        // echo "Qualifying text: {$node->nodeValue}\n";
        if (preg_match_all('~\b\d{5}\b~', $node->nodeValue, $matches)) {
            $partnos = array_merge($partnos, $matches[0]); //or array_push($partnos, ...$matches[0]);
        }
    }
    var_export($partnos);
    

    输出:

    array (
      0 => '67001',
      1 => '98765',
      2 => '50545',
      3 => '50525',
      4 => '50520',
      5 => '50555',
      6 => '50575',
    )
    

    xpath 查询说:

    //p                  #find p tags at any level/position in the dom
    [starts-with(.       #with a substring at the start of the node's text
    , 'Part Number: ')]  #that literally matches "Part Number: "
    

    正则表达式模式使用word boundary metacharacters (\b) 来区分部件号和非部件号。如果您因为问题中未包含某些数据而需要调整模式,请告诉我,我会提供进一步的指导。

    最后,我确实使用了一个纯正则表达式解决方案,该解决方案将\G 合并到Part Number: 或之前的匹配之后“继续”匹配,但是这种类型的模式有点难以概念化,并且再次使用 dom 解析器在处理有效 html 时,它是一种比正则表达式更稳定的工具。

    【讨论】:

    • 我想你已经回答了我的问题。您的解决方案是一种改进。但是,如果捕获组的数量未知,则并不总是可以在一个正则表达式中完成所有操作。两个阶段很容易实现。我会继续这样做。
    • @Kevin 今天我收到了对这个答案的支持,这让我的注意力回到了这里。我意识到我最初的答案并没有提供一年后我所拥有的卓越智慧。请查看我完全重写的答案,如果这就是您要找的,请告诉我。
    【解决方案2】:

    如果我正确理解了您的问题,您应该可以这样做:

    Part\sNumber:\s(\d{5})

    鉴于您的字符串包含所有Part Number,如下所示:

    <p>Part Number: 67001</p>
    
    <p>Part Number: 50545 &ndash; 450g Cartridge 50525 - 2.5kg Tub 50520 - 20kg Pail 50555 - 55kg Drum 50575 *Indent - 175kg Drum</p>
    
    <p>Part Number: 23425 - 55kg Drum 50575 *Indent - 175kg Drum</p>
    
    <p>Part Number: 52232</p>
    

    【讨论】:

    • 不,这个正则表达式只返回 {

      Part Number:} 我想要结束之前的所有代码{

      }
      之后的第一个代码
    • 如果它是页面上唯一的 5 位代码,那么您可以使用 (\d{5})
    • 不幸的是,5 位数的模式遍布整个网页,所以我不能只使用 (\d{5})
    猜你喜欢
    • 1970-01-01
    • 2017-09-13
    • 2011-03-11
    • 2017-01-07
    • 2019-12-19
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    相关资源
    最近更新 更多