【问题标题】:Find items where matched group is more / less than using regex查找匹配组多于/少于使用正则表达式的项目
【发布时间】:2023-03-15 22:20:02
【问题描述】:

给定以下文本:

<p style="color: blue">Some text</p>
<p style="color:blue; margin-left: 10px">* Item 1</p> // Should match
<p style="margin-left: 10px">* Item 2</p>
<p style="margin-left: 20px">* Sub Item 1a</p> // Should match
<p style="margin-left: 20px">* Sub Item 2a</p>
<p style="margin-left: 10px">* Item 3</p>
<p style="margin-left: 20px">* Sub Item 1b</p> // Should match
<p style="margin-left: 20px">* Sub Item 2b</p>
<p style="margin-left: 30px">* Sub Item 1c</p> // Should match
<p>Some text</p>
<p style="color:blue; margin-left: 10px">* Item 1</p> // Should match

我正在尝试查找任何符合以下条件的 p 元素:

  • 以星号字符开头
  • 他们有一个margin-left 内联样式
  • 前面的内容是:
    • p 元素没有左边距
    • p 元素的 margin-left 低于匹配的元素
    • 任何其他元素

所以在示例中,我需要匹配以下元素:

<p style="color:blue; margin-left: 10px">* Item 1</p> (preceding element is a p but doesn't have any margin-left)
<p style="margin-left: 20px">* Sub Item 1a</p> (preceding element is a p but has a different margin-left value)
<p style="margin-left: 20px">* Sub Item 1b</p> (preceding element is a p but has a different margin-left value)
<p style="margin-left: 30px">* Sub Item 1c</p> (preceding element is a p but has a margin-left value lower than the current matched element)
<p style="color:blue; margin-left: 10px">* Item 1</p> (preceding element is a p but has no margin-left value)

我无法使用DomDocument,因为我收到的标记并不总是有效的标记(通常来自Microsoft Office > HTML 转换),所以我正在使用正则表达式来解决问题。

我目前的正则表达式是:

(?!<p style=".*?(margin-left:\s?(?!\k'margin')px;).*?">\* .*?<\/p>)<p style="(?P<styles>.*?)margin-left:\s?(?P<margin>[0-9]{1,3})px;?">\* (?P<listcontent>.*)<\/p>

但这仅基于现有的前面元素是 pmargin-left 匹配。

如何将匹配的margin-left 组考虑在内并返回大于前一个匹配的值?

我创建了一个online regex 来演示问题,其中包含示例数据和我当前的输出。

【问题讨论】:

  • 这必须在单遍/方法中完成吗?你能匹配所有标签然后使用PHP减少集合吗?
  • 因为它是更大的一系列操作的一部分,如果可能的话,它需要作为一个单一的正则表达式来完成。
  • 我不相信这在一次通过中是可能的,因为您需要将值与其他值进行比较。您将能够找到包含margin-leftp 元素,但您需要一个辅助过程来进行比较。
  • 如果没有办法将其作为一个正则表达式来解决,并且您有办法使用两遍来解决它,请将其写下来作为答案。
  • 我会写一个答案,一个问题:最后一个元素如何匹配?它前面有一个没有边距的 p 元素,因此它未通过测试“除 p 元素之外的任何元素”?

标签: php regex pcre regex-negation regex-lookarounds


【解决方案1】:

此代码按预期工作,使用正则表达式抓取每个元素,然后循环遍历它们并检查业务逻辑:

<?php

$data = '<p style="color: blue">Some text</p>
<p style="color:blue; margin-left: 10px">* Item 1</p>
<p style="margin-left: 10px">* Item 2</p>
<p style="margin-left: 20px">* Sub Item 1a</p>
<p style="margin-left: 20px">* Sub Item 2a</p>
<p style="margin-left: 10px">* Item 3</p>
<p style="margin-left: 20px">* Sub Item 1b</p>
<p style="margin-left: 20px">* Sub Item 2b</p>
<p style="margin-left: 30px">* Sub Item 1c</p>
<div>Some text</div>
<p style="color:blue; margin-left: 10px">* Item 1</p>';

// Get all HTML tags, the element in [1], the attributes (style etc) in [2], the content in [3]
preg_match_all("/<(\w+)\b([^>]+)*>(.*?)<\/\w+>/", $data, $matches);

$results = [];

// Keep track of last element margin-left, if it's is missing it will be set to 0 making the next
// element included automatically if it has a margin-left
$lastMarginLeft = 0;

// Loop through matches and apply business rules
for ($i = 0; $i <= count($matches[0]); $i++) {
    /**
     * Business rules:
     * - Contents begins with an asterisk character
     * - Elements have a margin-left inline style
     * - The preceding content is either:
     *   - A p element which has no margin-left
     *   - A p element with a margin-left which is lower than the matched element
     *   - Any other element
     */

    // Assume no margin-left found by default
    $marginLeft = 0;

    // Check element has a margin-left
    if (strpos($matches[2][$i], 'margin-left') !== false) {
        // Extract margin-left value
        preg_match("/margin-left:\s?(\d+)/", $matches[2][$i], $value);
        $marginLeft = isset($value[1]) ? $value[1] : 0;

        // Check if this margin is greater than the last
        if ($marginLeft > $lastMarginLeft) {
            // Check content
            if (strpos($matches[3][$i], '*') === 0) {
                $results[] = $matches[0][$i];
            }
        }
    }

    // Capture margin left for next run
    $lastMarginLeft = $marginLeft;
}

// Results:
// Array
// (
//     [0] => <p style="color:blue; margin-left: 10px">* Item 1</p>
//     [1] => <p style="margin-left: 20px">* Sub Item 1a</p>
//     [2] => <p style="margin-left: 20px">* Sub Item 1b</p>
//     [3] => <p style="margin-left: 30px">* Sub Item 1c</p>
//     [4] => <p style="color:blue; margin-left: 10px">* Item 1</p>
// )

【讨论】:

    猜你喜欢
    • 2015-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-08
    • 1970-01-01
    • 2019-10-07
    • 2013-10-23
    • 1970-01-01
    相关资源
    最近更新 更多