【问题标题】:PHP preg_match_all - extract content from pattern in different orderPHP preg_match_all - 以不同的顺序从模式中提取内容
【发布时间】:2020-08-11 07:12:24
【问题描述】:

我正在清理我的代码中的一些 wordpress 短代码,我正在寻找一种无论值的顺序如何都能提取正确值的解决方案。

例子:

[Links label="my_label" url="my_url" external="other_value"]

如果我想提取 my_label、my_url 和 other_value,我会使用以下结构:

preg_match_all('/\[Links label=\"(.*?)\" url=\"(.*?)\" external=\"(.*?)\"\]/', $content, $output_array);

问题是我有时会有这样的不同顺序:

[Links url="my_url" external="other_value" label="my_label"]

我以前的 preg_match_all 不适用于此。我试图将每个模式放在 (...) 之间或使用 |但我没有得到预期的结果。我在这里看到了识别字符串的解决方案,但我需要的不仅仅是识别字符串,我还需要提取值。

这对于正则表达式专家来说可能是微不足道的。

谢谢

【问题讨论】:

  • 但是你的数组也会有不同的顺序。那么你怎么知道 URL 是第一个呢?
  • @Andreas 那是我的问题,有时 url 将是第一个,有时它会是别的东西。无论顺序如何,我都想提取数据。
  • 您是否试图仅提取这些属性的值?可能这就是你想要的吗? (\".*?\")你可以在这里查看-regex101.com/r/02Rqj6/1
  • @rootkonda 是的,我需要重视,但我需要知道哪个是标签,哪个是 url,...我总是可以根据内容识别它们,但外部和标签是字母数字和内容可能相似。

标签: php regex preg-match-all


【解决方案1】:

如果属性也可以以任何顺序为不同的数量并且应该以[Links 开头,您可以使用\G 锚点。键在捕获组 1 中,值在捕获组 2 中。

(?:\[Links|\G(?!^))(?=[^][]*])\h+([^\s=]+)="([^\s"]+)"

说明

  • (?:非捕获组
    • \[Links匹配[Links
    • |或者
    • \G(?!^)在上一场比赛结束时断言位置,而不是在开始时
  • )关闭非捕获组
  • (?=[^][]*]) 正向前瞻,在右侧断言 ]
  • \h+ 匹配 1+ 个水平空白字符
  • ( 捕获第 1 组
    • [^\s=]+ 匹配除 = 或空白字符以外的任何字符 1 次以上
  • )关闭第一组
  • =" 字面匹配
  • ( 捕获第 2 组
    • [^\s"]+ 匹配除 " 或空白字符以外的任何字符 1 次以上
  • )" 关闭第 2 组并匹配 "

Regex demo

例子

$re = '/(?:\[Links|\G(?!^))(?=[^][]*])\h+([^\s=]+)="([^\s"]+)"/m';
$str = '[Links label="my_label" url="my_url" external="other_value"]';

preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0);
print_r($matches);

输出

Array
(
    [0] => Array
        (
            [0] => [Links label="my_label"
            [1] => label
            [2] => my_label
        )

    [1] => Array
        (
            [0] =>  url="my_url"
            [1] => url
            [2] => my_url
        )

    [2] => Array
        (
            [0] =>  external="other_value"
            [1] => external
            [2] => other_value
        )

)

Php demo

【讨论】:

  • 精彩,一如既往!
  • @Toto 谢谢你,你真好 :-) 我从你的模式和 SO 的其他伟大模式中学到了很多
  • 哎哟,我想我脑子里的保险丝炸了:) 谢谢,太棒了!
【解决方案2】:

您可以(也许)做的就是不列出您想要匹配的键,只列出等号前后的任何内容。
这样你就可以“解析”字符串,然后可以弄清楚是什么。

$str = '[Links label="my_label" url="my_url" external="other_value"]';

preg_match("/\[links\s+(.*?)=\"(.*?)\"\s+(.*?)=\"(.*?)\"\s+(.*?)=\"(.*?)\"/i", $str, $match);

unset($match[0]);
foreach(array_chunk($match,2) as $m){
    $res[$m[0]] = $m[1];
}

var_dump($res);

这给了你:

array(3) {
  ["label"]=>
  string(8) "my_label"
  ["url"]=>
  string(6) "my_url"
  ["external"]=>
  string(11) "other_value"
}

https://3v4l.org/H1qGD

但这一切都取决于你是否有更多的东西要解析,那么也许这也会匹配其他东西。

【讨论】:

  • @Vishnu 感谢您的编辑。我批准了它,而是在正则表达式中使用 \s+ 。这将做同样的事情,但没有额外的函数调用。
  • 感谢您的建议!
【解决方案3】:

以上答案有效。但是如果你只需要值而不是它们对应的键,那么你也可以使用下面的代码。

$content = '[Links label="my_label" url="my_url" external="other_value"]';
$temp = explode("\"",$content);
$output = [];
for ($x = 0; $x < count($temp); $x++) {
    if($x % 2 != 0) { 
       array_push($output,$temp[$x]);
    }
}

$output 数组将包含所有值。

【讨论】:

    【解决方案4】:

    如果你想走完全的矫枉过正的路线,你可以重复使用 Wordpress 的正则表达式和处理。

    例如:

    <?php
    
    $res = extract_specific_shortcode('links', $teststring = '[links label="Label" url="https://nisamerica.com/" external="yes" /] '."\n".
    '[links label="Label2" url="https://google.com/" external="no"]content[/links]' );
    
    print_r($res);
    
    function extract_specific_shortcode( $tagname, $content ) { 
    
        $tagname_regex = preg_quote($tagname, '/');
    
        $wp_shortcode_atts = function( $text ) {
            $atts    = array();
            $pattern = '/([\w-]+)\s*=\s*"([^"]*)"(?:\s|$)|([\w-]+)\s*=\s*\'([^\']*)\'(?:\s|$)|([\w-]+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|\'([^\']*)\'(?:\s|$)|(\S+)(?:\s|$)/';
            $text    = preg_replace( "/[\x{00a0}\x{200b}]+/u", ' ', $text );
            if ( preg_match_all( $pattern, $text, $match, PREG_SET_ORDER ) ) {
                foreach ( $match as $m ) {
                    if ( ! empty( $m[1] ) ) {
                        $atts[ strtolower( $m[1] ) ] = stripcslashes( $m[2] );
                    } elseif ( ! empty( $m[3] ) ) {
                        $atts[ strtolower( $m[3] ) ] = stripcslashes( $m[4] );
                    } elseif ( ! empty( $m[5] ) ) {
                        $atts[ strtolower( $m[5] ) ] = stripcslashes( $m[6] );
                    } elseif ( isset( $m[7] ) && strlen( $m[7] ) ) {
                        $atts[] = stripcslashes( $m[7] );
                    } elseif ( isset( $m[8] ) && strlen( $m[8] ) ) {
                        $atts[] = stripcslashes( $m[8] );
                    } elseif ( isset( $m[9] ) ) {
                        $atts[] = stripcslashes( $m[9] );
                    }
                }
         
                // Reject any unclosed HTML elements.
                foreach ( $atts as &$value ) {
                    if ( false !== strpos( $value, '<' ) ) {
                        if ( 1 !== preg_match( '/^[^<]*+(?:<[^>]*+>[^<]*+)*+$/', $value ) ) {
                            $value = '';
                        }
                    }
                }
            } else {
                $atts = ltrim( $text );
            }
         
            return $atts;
        };
    
        // Taken from wordpress 
        $regex = '/\\['                             // Opening bracket.
            . '(\\[?)'                           // 1: Optional second opening bracket for escaping shortcodes: [[tag]].
            . "($tagname_regex)"                     // 2: Shortcode name.
            . '(?![\\w-])'                       // Not followed by word character or hyphen.
            . '('                                // 3: Unroll the loop: Inside the opening shortcode tag.
            .     '[^\\]\\/]*'                   // Not a closing bracket or forward slash.
            .     '(?:'
            .         '\\/(?!\\])'               // A forward slash not followed by a closing bracket.
            .         '[^\\]\\/]*'               // Not a closing bracket or forward slash.
            .     ')*?'
            . ')'
            . '(?:'
            .     '(\\/)'                        // 4: Self closing tag...
            .     '\\]'                          // ...and closing bracket.
            . '|'
            .     '\\]'                          // Closing bracket.
            .     '(?:'
            .         '('                        // 5: Unroll the loop: Optionally, anything between the opening and closing shortcode tags.
            .             '[^\\[]*+'             // Not an opening bracket.
            .             '(?:'
            .                 '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag.
            .                 '[^\\[]*+'         // Not an opening bracket.
            .             ')*+'
            .         ')'
            .         '\\[\\/\\2\\]'             // Closing shortcode tag.
            .     ')?'
            . ')'
            . '(\\]?)/i';                          // 6: Optional second closing brocket for escaping shortcodes: [[tag]].
        // phpcs:enable
    
    
        preg_match_all($regex, $content, $matches, PREG_SET_ORDER);
        $set = [];
        foreach($matches as $match) {
            $set[] = [
                'fullmatch' => $match[0],
                'attributes' => $wp_shortcode_atts($match[3]),
            ];
        }
        return $set;
    }
    

    产生以下输出:

    Array
    (
        [0] => Array
            (
                [fullmatch] => [links label="Label" url="https://nisamerica.com/" external="yes" /]
                [attributes] => Array
                    (
                        [label] => Label
                        [url] => https://nisamerica.com/
                        [external] => yes
                    )
    
            )
    
        [1] => Array
            (
                [fullmatch] => [links label="Label2" url="https://google.com/" external="no"]content[/links]
                [attributes] => Array
                    (
                        [label] => Label2
                        [url] => https://google.com/
                        [external] => no
                    )
    
            )
    
    )
    

    以上代码来源于以下函数:

    与发布的其他解决方案一样,WordPress 将其属性映射分为两部分:收集键和值,然后将它们组合在一起。他们的正则表达式比这里展示的要多一些,因为它处理的边缘情况当然要多一些。

    【讨论】:

      【解决方案5】:

      你也可以这样试试:

      preg_match_all('/(\b[^"=]+)="([^"]+)"/', $content, $output_array);
      
      $result = array_combine($output_array[1], $output_array[2]);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-17
        相关资源
        最近更新 更多