Анализ CSS по регулярному выражению

Я создаю редактор CSS и пытаюсь создать регулярное выражение, которое может получать данные из документа CSS. Это регулярное выражение работает, если у меня есть одно свойство, но я не могу заставить его работать для всех свойств. Я использую синтаксис preg / perl в PHP.

Regex

(?<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 ) ) 

Заранее спасибо за любую помощь – это меня сбивало с толку весь день!

Это просто кажется слишком запутанным для одного регулярного выражения. Ну, я уверен, что с правильной протяженностью продвинутый пользователь может создать правильное регулярное выражение. Но тогда вам понадобится еще более продвинутый пользователь для его отладки.

Вместо этого я бы предложил использовать регулярное выражение, чтобы вытащить фрагменты, а затем повторить каждую штуку отдельно. например,

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

Затем вы получите селектор и атрибуты в отдельных полях, а затем разделите их. (Даже селектор будет забавным для синтаксического анализа.) Обратите внимание, что даже это будет иметь боль, если} может появляться внутри кавычек или что-то в этом роде. Вы могли бы, опять же, выкрутить черту из этого, чтобы избежать этого, но, вероятно, даже лучше избегать здесь регулярного выражения и обрабатывать его, анализируя одно поле за раз, возможно, используя парсер с рекурсивным спусканием или yacc / bison или без разницы.

Вы пытаетесь вытащить структуру из данных, а не только отдельных значений. Регулярные выражения могут быть тягостно растянуты, чтобы выполнить эту работу, но вы действительно входите в парсерную территорию и должны вытаскивать большие пушки, а именно парсеры.

Я никогда не использовал инструменты генерации парсера PHP, но они выглядят хорошо после легкого сканирования документов. Проверьте LexerGenerator и ParserGenerator . LexerGenerator возьмет кучу регулярных выражений, описывающих различные типы токенов на языке (в данном случае CSS), и выплюнет код, который распознает отдельные токены. ParserGenerator возьмет грамматику, описание того, что на языке составлено из чего-то другого, и выплюнет парсер, код, который берет кучу токенов и возвращает дерево синтаксиса (структура данных, которой вы пользуетесь.

Не используйте собственное регулярное выражение для синтаксического анализа CSS. Зачем изобретать колесо, пока вас ждет код, готовый к использованию и (надеюсь) без ошибок?

Есть два общедоступных класса, которые могут анализировать CSS для вас:

Пакет HTML_CSS PEAR на pear.php.net

а также

Класс CSS Parser в PHPCLasses:

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

Я бы рекомендовал не использовать регулярные выражения для анализа 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", "");} (похоже, это отладочная вещь, которая осталась)

Я отразил сценарий здесь , с приведенными выше изменениями в

Я использую регулярное выражение ниже, и это в значительной степени работает … конечно, этот вопрос уже старен, и я вижу, что вы отказались от своих усилий … но в случае, если кто-то еще пробегает по нему:

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

(hafta удаляет все / * комментарии * / из вашего CSS сначала, чтобы быть в безопасности)

Я написал фрагмент кода, который легко анализирует 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 ) ) 

Попробуй это

 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";