12

CSS エディターを作成していて、CSS ドキュメントからデータを取得できる正規表現を作成しようとしています。この正規表現は、1 つのプロパティがある場合に機能しますが、すべてのプロパティに対して機能させることはできません。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

CSSの解析に独自の正規表現を使用しないでください。待っているコードがあり、すぐに使用でき、(うまくいけば)バグがないのに、なぜ車輪の再発明をするのですか?

CSSを解析できる一般的に利用可能なクラスは2つあります。

pear.php.netのHTML_CSSPEARパッケージ

PHPCLassesのCSSパーサークラス:

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

于 2009-06-18T13:11:22.693 に答える
11

個々の値だけでなく、データから構造を引き出そうとしています。正規表現は、その仕事をするために痛々しいほど引き伸ばされる可能性がありますが、実際にはパーサーの領域に入りつつあり、大砲、つまりパーサーを引き抜く必要があります。

PHP パーサー生成ツールを使用したことはありませんが、ドキュメントを軽くスキャンしただけでは問題ないように見えます。LexerGeneratorParserGeneratorを確認してください。LexerGenerator は、言語 (この場合は CSS) のさまざまな種類のトークンを記述する正規表現の束を受け取り、個々のトークンを認識するコードを吐き出します。ParserGenerator は、言語のどのようなものが他のものから構成されているかの記述である文法を取得し、トークンの束を取得して構文ツリー (目的のデータ構造) を返すコードであるパー​​サーを吐き出します。

于 2008-10-25T20:43:39.083 に答える
8

特に単一の正規表現では、正規表現を使用して CSS を解析しないことをお勧めします。

正規表現で解析を行うことを主張する場合は、適切なセクションに分割します。1 つの正規表現を使用してすべての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 の配列を取得するために a を実行することだけです。

$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

キー:値のペア; さらに複雑化した正規表現の落とし穴

私もキーと値のペアを区切る作業を試み始めましたが、セレクターごとに複数のスタイルがある場合、物事が思ったよりも複雑になり始めることがすぐにわかりました。key:values を区切ろうとした正規表現のバージョン 1 と、ここで複数の宣言で失敗した方法を確認できます: https://regex101.com/r/qQRIHx/1

実装

他の人が述べたように、CSS を解析してトークン化するには、これを複数のステップに分割する必要があります。この正規表現は宣言を取得するのに役立ちますが、それらを解析する必要があります。

宣言パーサー

最初の一致セットを取得した後、このようなものを使用して宣言を解析できます。

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

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

エッジケース

上記の例は複数の宣言でうまく機能しますが、[ほとんどの] ブラウザーでレンダリングされるが、この正規表現を破るセミコロンで終わらない 1 つの宣言だけである可能性があります。

特筆すべき事例

メディア クエリがある場合は、ネストされたルールを考慮する必要がある場合もあります。この場合、抽出された宣言に対して css マッチング正規表現を実行しようとします。一致した場合は、その上で再帰を実行できます (バニラ CSS で複数のレベルがネストされる場合があるかどうかはわかりませんが)。

エッジケース
  • これは、文字列内の右中括弧を処理しません

明日の研究

css代わりにorのような npm パッケージを使用することにしましたcssom。これがPHPであることは知っていますが、私にとっては多くの負担がかかり、遭遇し続けるエッジケースを処理することになります。

編集

Jotform の public css.js ライブラリを使用することになりました。CSS を解析するライブラリを選択する際の主な要件の 1 つであったフットプリントが非常に小さいです。

于 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 に答える