12

我正在创建一个 CSS 编辑器,并试图创建一个可以从 CSS 文档中获取数据的正则表达式。如果我有一个属性,但我无法让它适用于所有属性,则此正则表达式有效。我在 PHP 中使用 preg/perl 语法。

正则表达式

(?<selector>[A-Za-z]+[\s]*)[\s]*{[\s]*((?<properties>[A-Za-z0-9-_]+)[\s]*:[\s]*(?<values>[A-Za-z0-9#, ]+);[\s]*)*[\s]*}

测试用例

body { background: #f00; font: 12px Arial; }

预期结果

Array(
    [0] => Array(
            [0] => body { background: #f00; font: 12px Arial; }
            [selector] => Array(
                [0] => body
            )
            [1] => Array(
                [0] => body
            )
            [2] => font: 12px Arial; 
            [properties] => Array(
                [0] => font
            )
            [3] => Array(
                [0] => font
            )
            [values] => Array(
                [0] => 12px Arial
                [1] => background: #f00
            )
            [4] => Array(
                [0] => 12px Arial
                [1] => background: #f00
            )
        )
)

实际结果

Array(
    [0] => Array
        (
            [0] => body { background: #f00; font: 12px Arial; }
            [selector] => body 
            [1] => body 
            [2] => font: 12px Arial; 
            [properties] => font
            [3] => font
            [values] => 12px Arial
            [4] => 12px Arial
        )
    )

提前感谢您的帮助 - 这让我整个下午都感到困惑!

4

8 回答 8

20

对于单个正则表达式来说,这似乎太复杂了。好吧,我确信通过正确的扩展,高级用户可以创建正确的正则表达式。但是你需要一个更高级的用户来调试它。

相反,我建议使用正则表达式来提取碎片,然后分别标记每个碎片。例如,

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

然后你最终得到选择器和不同字段中的属性,然后将它们分开。(即使选择器解析起来也会很有趣。)请注意,如果 } 可以出现在引号或其他内容中,即使这样也会很痛苦。您可以再次将其复杂化以避免这种情况,但最好在这里完全避免正则表达式,并通过一次解析一个字段来处理它,也许通过使用递归下降解析器或 yacc/bison 或任何。

于 2008-10-25T20:42:06.810 回答
11

您正试图从数据中提取结构,而不仅仅是单个值。正则表达式可能会很痛苦地完成这项工作,但你真的进入了解析器领域,应该拿出大枪,即解析器。

我从未使用过 PHP 解析器生成工具,但在对文档进行简单扫描后,它们看起来还不错。查看LexerGeneratorParserGenerator。LexerGenerator 将采用一堆正则表达式来描述一种语言(在本例中为 CSS)中不同类型的标记,并生成一些识别各个标记的代码。ParserGenerator 将采用语法,一种语言中的哪些事物由哪些其他事物组成的描述,并吐出一个解析器,该代码采用一堆标记并返回语法树(您所追求的数据结构。

于 2008-10-25T20:43:39.083 回答
11

不要使用您自己的正则表达式来解析 CSS。为什么要在有代码等着你、准备好使用并且(希望)没有错误的时候重新发明轮子?

有两个通用类可以为您解析 CSS:

pear.php.net 上的 HTML_CSS PEAR 包

PHPCLasses 上的 CSS Parser 类:

http://www.phpclasses.org/browse/package/1289.html

于 2009-06-18T13:11:22.693 回答
8

我建议不要使用正则表达式来解析 CSS——尤其是在单个正则表达式中!

如果您坚持在正则表达式中进行解析,请将其拆分为合理的部分 - 使用一个正则表达式来拆分所有body{..}块,然后使用另一个来解析color:rgb(1,2,3);属性。

如果您实际上是在尝试编写“有用”的东西(而不是尝试学习正则表达式),请寻找预先编写的 CSS 解析器。

我发现这个 cssparser.php似乎工作得很好:

$cssp = new cssparser;
$cssp -> ParseStr("body { background: #f00;font: 12px Arial; }");
print_r($cssp->css);

..输出以下内容:

Array
(
    [body] => Array
        (
            [background] => #f00
            [font] => 12px arial
        )
)

解析器非常简单,所以应该很容易弄清楚它在做什么。哦,我不得不删除读取的行if($this->html) {$this->Add("VAR", "");}(它似乎是一个调试的东西留在里面)

我在这里镜像了脚本,上面的更改在

于 2008-10-26T02:06:30.587 回答
6

我正在使用下面的正则表达式,它几乎可以工作......当然这个问题现在很老了,我看到你已经放弃了你的努力......但万一其他人遇到它:

(?<selector>(?:(?:[^,{]+),?)*?)\{(?:(?<name>[^}:]+):?(?<value>[^};]+);?)*?\}

(为了安全起见,首先从您的 CSS中删除所有/* 注释 */ )

于 2010-04-22T20:11:48.370 回答
6

我写了一段很容易解析 CSS 的代码。你所要做的就是做一些真正的爆炸...... $css 变量是 CSS 的字符串。你所要做的就是print_r($css)得到一个很好的 CSS 数组,完全解析。

$css_array = array(); // master array to hold all values
$element = explode('}', $css);
foreach ($element as $element) {
    // get the name of the CSS element
    $a_name = explode('{', $element);
    $name = $a_name[0];
    // get all the key:value pair styles
    $a_styles = explode(';', $element);
    // remove element name from first property element
    $a_styles[0] = str_replace($name . '{', '', $a_styles[0]);
    // loop through each style and split apart the key from the value
    $count = count($a_styles);
    for ($a=0;$a<$count;$a++) {
        if ($a_styles[$a] != '') {
            $a_key_value = explode(':', $a_styles[$a]);
            // build the master css array
            $css_array[$name][$a_key_value[0]] = $a_key_value[1];
        }
    }               
}

给你这个:

Array
(
    [body] => Array
        (
            [background] => #f00
            [font] => 12px arial
        )
)
于 2011-03-29T19:27:03.883 回答
2

在 Tanktalus 的当前答案的基础上,有一些改进和边缘情况需要注意。

CSS 解析正则表达式

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

此正则表达式将对本示例中列出的一些其他边缘情况进行一些空间修剪和命中:https ://regex101.com/r/qQRIHx/5

键:值对;更复杂的正则表达式的陷阱

我也开始尝试界定键:值对,但很快发现在每个选择器有多种样式的情况下,事情开始变得比我想要的更棘手。您可以在此处查看我尝试分隔键的正则表达式的版本 1:值以及它如何因多个声明而失败:https ://regex101.com/r/qQRIHx/1

执行

正如其他人提到的,您应该将其分解为多个步骤来解析和标记您的 css。此正则表达式将帮助您获得声明,但您需要将其解析出来。

声明解析器

在获得第一组匹配项后,您可以使用类似的方法来解析声明。

([^:\s]+)*\s*:\s*([^;]+);

示例:https ://regex101.com/r/py9OKO/1/

边缘案例

上面的示例适用于多个声明,但它可能只是 1 个没有分号结尾的声明,它将在 [大多数] 浏览器中呈现,但会破坏这个正则表达式。

著名案例

如果有媒体查询,您可能还需要考虑嵌套规则。在这种情况下,我会尝试针对提取的声明运行 css 匹配正则表达式。如果你得到匹配,你可以在它上面运行递归(尽管我不确定在某些情况下你会为 vanilla CSS 嵌套超过 1 级)。

边缘案例
  • 这不处理字符串中的右花括号

明天的研究

我决定改用 npm 包,如cssor cssom。我知道这是在 PHP 中,但它会为我做很多繁重的工作并处理我不断遇到的边缘情况。

编辑

我最终使用了 Jotform 的公共 css.js 库。它的占用空间非常小,这是我在选择库来解析 CSS 时的主要要求之一。

于 2018-02-22T20:57:34.797 回答
0

试试这个

function trimStringArray($stringArray){
    $result = array();
    for($i=0; $i < count($stringArray); $i++){
        $trimmed = trim($stringArray[$i]);
        if($trimmed != '') $result[] = $trimmed;
    }
    return $result;
}
$regExp = '/\{|\}/';
$rawCssData = preg_split($regExp, $style);

$cssArray = array();
for($i=0; $i < count($rawCssData); $i++){
    if($i % 2 == 0){
        $cssStyle['selectors'] = array();
        $selectors = split(',', $rawCssData[$i]);
        $cssStyle['selectors'] = trimStringArray($selectors);
    }
    if($i % 2 == 1){
        $attributes = split(';', $rawCssData[$i]);
        $cssStyle['attributes'] = trimStringArray($attributes);
        $cssArray[] = $cssStyle;
    }

}
//return false;
echo '<pre>'."\n";
print_r($cssArray);
echo '</pre>'."\n";
于 2010-05-09T17:54:10.143 回答