• Jetzt anmelden. Es dauert nur 2 Minuten und ist kostenlos!

Regex - alles zwischen { }

Liskel

Mitglied
[erledigt] Regex - alles zwischen { }

Hallo :)

Ich würde gerne alles zwischen den besagten Klammern {} in einem String auslesen. Mein bisheriger Ansatz:

PHP:
preg_match_all("/\{([a-z]{1,})\}/", $properties, $a);

  • Müssen die Klammern escaped werden? (ohne \ gehts auch nicht...)
  • In $a sollten doch danach alle StringTeile die sich zwischen { } befunden haben gespeichert sein?

Momentan kommt nur ein leeres Array füR $a heraus.

Wäre über ne fixe Hilfe erfreut. Danke!
 
Zuletzt bearbeitet:
Werbung:
Grundsätzlich geht es um das Filtern von CSS-Eigenschaften, also zum Beispiel
Code:
#head{
	width: 1014px;
	height: 34px;
	background: #D3D3D3;
	padding: 5px;
	border-style: solid;
	border-width: 5px;
	border-color: #262626;
} 

#navigation{
	list-style-type: none;
	display: inline;
	vertical-align: bottom;
} 

#navigation li{
	display: block;
	position: relative;
	float: left;
}

Hätte halt jetzt gerne nen Array mit
  • #head{ ...}
  • #navigation{ ..}
  • #navigation li{ ..} [<-- hier dann mein nächstes Problem, momentan würde nur li{ ...} rauskommen!]

Bin mittlerweile auch selbst n Stück weitergekommen, nachdem ich eigentlich dachte, dass mein obiger Vorschlag schon halbwegs Richtiges enthielt, hab ich erfahren müssen, dass ich Regex doch nicht verstanden habe (wer tut das schon ... :D)
Jedenfalls haut folgendes jetzt ersteinmal hin um die ersten beiden Teile herauszufinden.

PHP:
preg_match_all("/.*?(\\{).*?(\\})/is", $properties, $a);

Für den #navigation li{...} wär ich für Anregungen sehr dankbar :)

Edit: Das ganze hat sich soweit erledigt! Auch mein übrig gebliebenes Problem lässt sich mit oben genanntem preg_match_all() lösen. Bisher hatte ich in einem Schritt davor ein kleines Problem eingebaut und damit einen falschen Aufbau von $properties gehabt.
 
Zuletzt bearbeitet:
Werbung:
Weiß nicht genau, was du matchen willst, aber aktuell würde es wohl auch /[^}]*}/ tun. Beliebig viele Zeichen, die nicht "}" sind, gefolgt von einem "}".

Es ist aber schwierig bis unmöglich, so was korrekt mit Regex hinzubekommen. Kommentare oder Strings können ja auch geschweifte Klammern enthalten.

Für so was ist ein Tokenizer/Parser/Automat besser geeignet. Du durchläufst die Eingabe sozusagen zeichenweise und speicherst immer, ob du aktuell gerade in einem String oder in einem Kommentar bist. Falls ja, werden Klammern und ähnliches entsprechend ausgewertet oder ignoriert. So was gibt es aber sicher schon fertig. Du könntest dich mal unter den Syntax-Highlightern umsehen.
 
Du suchst denke ich mal sowas hier:

Christian Wederka - Webentwicklung Rostock :: CSS Parser DEMO

Das war mal etwas, was ich vor ca. einem Jahr für ein CMS geschrieben hatte, dass ähnlich wie im MyBB-Backend die CSS-Files einfacher bearbeiten konnte. Meine Lösung funktioniert hier mit RegEx, wobei der Ausdruck selber garantiert jenseits von gut und böse ist (hab Reguläre Ausdrücke bis heute kaum verstanden), aber den kannste ja austauschen. Fehler sind mir bisher keine aufgefallen, aber garantieren kann ich das nicht. War ja schließlich auch nur für einen eingeschränkten Nutzerkreis gedacht. Aber der Ansatz von mermshaus ist denke ich mal auch der bessere Weg, wenn auch vielleicht rechenintensiver.
 
Code:
body {
    color: green;
    /* } */
    background: red;
}

Es sind immer die „Spezialfälle“, die so was kompliziert machen.

Intern wird die Regex-Engine sicherlich auch eine Art Automaten konstruieren, also die Eingabe zeichenweise durchgehen. Die Performance-Unterschiede ergeben sich dann vor allem dadurch, dass „PHP-interne“ Funktionen nativ kompiliert in C implementiert sind, was rein prinzipbedingt um Längen schneller ist als eigener PHP-Code.

Das würde ich aber – wie Performancefragen generell – nicht an sich als Maß aller Dinge ansehen.
 
Werbung:
newlord: Ja, ungefähr soweit bin ich jetzt auch! Konnte mir dennoch noch eine Vereinfachung bei dir abschauen;) danke!

mermshaus: Hmmm, leider hast du recht. Habe ähnliche Probleme schon kommen sehen, aber ähnlich wie bei newlord, soll das ganze eher für mich sein;)
Vielleicht werde ich das ganze nocheinmal zeichenweise probieren!

Interessante Infos von euch, dankeschön!
 
Ich habe mal ein wenig aus dem Handgelenk gebastelt. (Soll heißen: Das ist nicht unbedingt toll oder nachahmenswert geworden.) Vielleicht ist das als so eine Art Pseudo-Ansatz interessant für euch.

Das ist – vor allem theoretisch – alles nicht ganz trivial.

PHP:
<?php

class CssTokenizer
{
    protected $chars;
    protected $tokens;

    /**
     * Get character at index
     *
     * @param int $index
     * @return string
     */
    protected function gc($index)
    {
        $c = null;

        if ($index < count($this->chars)) {
            $c = $this->chars[$index];
        }

        return $c;
    }

    /**
     *
     * @param string $input
     */
    public function tokenize($input)
    {
        $this->chars = preg_split('/(?<!^)(?!$)/u', $input);
        $this->tokens = array();
        $n = 0;
        $length = mb_strlen($input);
        $isInComment  = false;
        $isInString   = false;
        $isInRuleSet = false;
        $stringDelimiter = '';
        $buffer = '';

        while ($n < $length) {
            $c = $this->chars[$n];

            switch ($c) {
                case '/':
                    if (!$isInComment && !$isInString) {
                        if ($this->gc($n + 1) === '*') {
                            $isInComment = true;
                            $this->tokens[] = array('default', $buffer);
                            $buffer = '';
                            $buffer .= $c . $this->gc($n + 1);
                            $n += 2;
                        } else {
                            $buffer .= $c;
                            $n++;
                        }
                    } else {
                        $buffer .= $c;
                        $n++;
                    }
                    break;

                case '*':
                    if ($isInComment) {
                        if ($this->gc($n + 1) === '/') {
                            $buffer .= $c . $this->gc($n + 1);
                            $n += 2;
                            $this->tokens[] = array('comment', $buffer);
                            $isInComment = false;
                            $buffer = '';
                        } else {
                            $buffer .= $c;
                            $n++;
                        }
                    } else {
                        $buffer .= $c;
                        $n++;
                    }
                    break;

                case '"':
                case '\'':
                    if (!$isInComment && !$isInString) {
                        $isInString = true;
                        $stringDelimiter = $c;
                        $this->tokens[] = array('default', $buffer);
                        $buffer = '';
                        $buffer .= $c;
                        $n++;
                    } else {
                        if ($isInString && $stringDelimiter === $c) {
                            $isInString = false;
                            $stringDelimiter = '';
                            $buffer .= $c;
                            $n++;
                            $this->tokens[] = array('string', $buffer);
                            $buffer = '';
                        } else {
                            $buffer .= $c;
                            $n++;
                        }
                    }
                    break;

                case '{':
                    if (!$isInComment && !$isInString) {
                        if (!$isInRuleSet) {
                            $isInRuleSet = true;
                            $this->tokens[] = array('default', $buffer);
                            $this->tokens[] = array('rule_start', $c);
                            $buffer = '';
                            $n++;
                        } else {
                            $this->tokens[] = array('default', $buffer);
                            $this->tokens[] = array('error', $c);
                            $buffer = '';
                            $n++;
                        }
                    } else {
                        $buffer .= $c;
                        $n++;
                    }
                    break;

                case '}':
                    if (!$isInComment && !$isInString) {
                        if ($isInRuleSet) {
                            $isInRuleSet = false;
                            $this->tokens[] = array('default', $buffer);
                            $this->tokens[] = array('rule_end', $c);
                            $buffer = '';
                            $n++;
                        } else {
                            $this->tokens[] = array('default', $buffer);
                            $this->tokens[] = array('error', $c);
                            $buffer = '';
                            $n++;
                        }
                    } else {
                        $buffer .= $c;
                        $n++;
                    }
                    break;

                default:
                    $buffer .= $c;
                    $n++;
                    break;
            }
        }

        if ($buffer !== '') {
            $this->tokens[] = array('default', $buffer);
        }

        // Remove empty tokens

        $oldTokens = $this->tokens;
        $this->tokens = array();

        foreach ($oldTokens as $token) {
            if ($token[1] !== '') {
                $this->tokens[] = $token;
            }
        }

        return $this->tokens;
    }
}

$input = <<<EOT
/* Mein tolles Stylesheet */
#head{
    width: 1014px;
    height: 34px;
    /* } */
    background: #D3D3D3;
    padding: 5px;
    content: "Hello 'World!'";
    border-style: solid;
    border-width: 5px;
    border-color: #262626;
}

/*
    "Hello 'World!'"

    'This "is" a test'

    /*

    test {
        content: 'Hello "World!"';
    }
*/
#navigation{
    list-style-type: none;
    display: inline;
    vertical-align: bottom;
    content: 'This "is" a test.';
}

/* Ein weiterer Kommentar */
#navigation li{
    display: block;
    position: relative;
    float: left;
}

selector {
    /* Rule set */
    {
}}
EOT;

$c = new CssTokenizer();
$tokens = $c->tokenize($input);

$output = '';

$e = function ($s) {
    return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
};

$output = '';

foreach ($tokens as $token) {
    list($class, $content) = $token;

    $output .= '<span class="' . $e($class) . '">' . $e($content) . '</span>';
}

?><!DOCTYPE html>

<html lang="en">

    <head>
        <meta charset="UTF-8" />
        <title>CSS Parser</title>
        <style>
/*<![CDATA[*/
pre {
    border: 1px solid #ccc;
    padding: 5px;
}

.string {
    background: #f0f0f0;
    color: green;
}

.comment {
    color: orange;
}

.rule_start, .rule_end {
    color: red;
}

.error {
    background: red;
    color: white;
}

/*]]>*/
        </style>
    </head>

    <body>

        <pre><code><?=$output?></code></pre>

    </body>

</html>
 
Schaut zumindest interessant aus. Ich hätte auf die einzelnen Zeichen per $input[$i] zugegriffen anstatt preg_split zu nutzen, aber letzteres geht natürlich auch.
 
Werbung:
Zurück
Oben