【问题标题】:Recursive/subroutine regex to match CSS media queries递归/子例程正则表达式以匹配 CSS 媒体查询
【发布时间】:2016-08-22 23:33:14
【问题描述】:

我正在寻找可以可靠地匹配媒体查询及其内容的正则表达式(在 PHP PCRE 中),包括媒体查询正文为空的有点奇怪的情况。源文本可能是:

@media only screen {
    p {
        color:red;
    }
}
@media only screen and (max-width: 596px) {
    p {
        color:blue;
    }
    img {
        max-width: 200px;
    }
}
@media only screen {

}
img {
    display: block;
}
@media only screen and (max-width: 240px) {
    p {
        color:green;
    }
}
p {
    font-weight: normal;
}

我想将每个媒体查询及其 CSS 主体捕获为子模式,所以我最终会得到一个 PHP 数组,如下所示:

[['@media only screen {
        p {
            color:red;
        }
    }','p {
            color:red;
        }'],...]

关键是这需要是递归或子例程模式才能平衡大括号。空查询足以混淆this question中的模式,因为它无法区分css规则的结尾和空媒体查询的结尾:

/@media[^{]+\{([\s\S]+?\})\s*\}/

我一直在尝试使用this article 中的建议来创建(b(?:m|(?1))*e) 形式的模式,但未能成功,其中b 是构造的开始,m 是中间可能发生的构造,e 是最后可能发生的,它们都不能匹配相同的东西。

所以,b应该是@media[^{]+\{e应该是\},而m需要消耗CSS规则,也许([^{]+?\{[^}]*?\s*\}),给我:

/(@media[^{]+\{(?:([^{]+?\{[^}]*?\}\s*)*|(?1))*\})/s

但是,这不起作用,所以我有点迷茫。谁能提出一个有效的模式?

我已经设置了一个正则表达式测试here

或者,非正则表达式解析器可能会更好。

请注意,我一般不会尝试验证或匹配 CSS 选择器(不是正则表达式的工作),只是获取查询的内容及其正文。

更新添加了更多示例内容,解释了我想要得到的内容。

【问题讨论】:

  • 不,它可以是任何有效的媒体查询。
  • @WiktorStribiżew 应该是[^{}]++ 还是[^{}]+
  • @rock321987:一个常用的非占有量词在这里也能起作用,任何一个都可以。
  • 非常接近,谢谢。我希望捕获的组略有不同,我已经更新了问题以表明这一点。

标签: php css regex recursion pcre


【解决方案1】:

如果您确定要匹配的块始终具有平衡数量的大括号,则可以使用带有子例程的正则表达式,如下所示:

'~@media\b[^{]*({((?:[^{}]+|(?1))*)})~'

regex demo

这是IDEONE demo

$re = '~@media\b[^{]*({((?:[^{}]+|(?1))*)})~'; 
$str = "@media only screen {\n    p {\n        color:red;\n    }\n}\n@media only screen and (max-width: 596px) {\n    p {\n        color:blue;\n    }\n    img {\n        max-width: 200px;\n    }\n}\n@media only screen {\n\n}\nimg {\n    display: block;\n}\n@media only screen and (max-width: 240px) {\n    p {\n        color:green;\n    }\n}\np {\n    font-weight: normal;\n}"; 
preg_match_all($re, $str, $matches, PREG_PATTERN_ORDER);
print_r($matches[0]);
print_r($matches[2]);

模式详情

  • @media\b - 将@media 匹配为一个完整的单词(因为\b 是一个单词边界)
  • [^{]* - 匹配除 { 之外的 0+ 个字符
  • ({((?:[^{}]+|(?1))*)}) - 一个捕获组 #1 捕获 {...} 块与 {} 的平衡数量(注意它是一个技术组,我们需要递归这个组子模式才能正确匹配 @987654335 @s)。它匹配...
    • { - 左大括号
    • ((?:[^{}]+|(?1))*) - 第 2 组(平衡 {...} 内的内容)匹配
      • [^{}]+ - 除{} 之外的1+ 个字符(因为我们需要匹配除前导和尾随分隔符之外的所有内容)
      • | - 或者...
      • (?1) - 整个 Group 1 子模式
    • } - 右大括号

请注意,$matches[2] 可以使用preg_match_all('~\s*(\w+)\s*{\s*([^}]*?)\s*}~', $matches[2], $subblocks) 模式进一步处理。

【讨论】:

    猜你喜欢
    • 2012-12-18
    • 1970-01-01
    • 1970-01-01
    • 2019-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-23
    • 1970-01-01
    相关资源
    最近更新 更多