递归/子例程正则表达式匹配CSS媒体查询

时间:2016-04-28 09:33:40

标签: php css regex recursion pcre

我正在寻找能够可靠地匹配媒体查询及其内容的正则表达式(在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是构造的开头,{{1是构造中间可能发生的事情,m是最后可能发生的事情,并且它们都不能匹配相同的东西。

因此,e应为b@media[^{]+\{应为e,而\}需要使用CSS规则,可能是m,给我:

([^{]+?\{[^}]*?\s*\})

然而,这不起作用,所以我有点迷失。任何人都可以提出有效的模式吗?

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

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

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

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

1 个答案:

答案 0 :(得分:3)

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

'~@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捕获平衡号为{...}{的{​​{1}}块(注意它是一个技术组,我们需要递归此组子模式,以便正确匹配} s)。它匹配...
    • {...} - 一个大括号
    • { - 第2组(平衡((?:[^{}]+|(?1))*)内)匹配的内容
      • {...} - [^{}]+{以外的1个以上字符(因为我们需要匹配不是前导和尾随分隔符的所有内容)
      • } - 或......
      • | - 整个第1组子模式
    • (?1) - 一个大括号

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