Анализ 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
)
)
Заранее благодарим за любую помощь - это меня сбивало с толку весь день!
Ответы
Ответ 1
Это кажется слишком запутанным для одного регулярного выражения. Ну, я уверен, что с правильной протяженностью продвинутый пользователь может создать правильное регулярное выражение. Но тогда вам понадобится еще более продвинутый пользователь для его отладки.
Вместо этого я бы предложил использовать регулярное выражение, чтобы вытащить фрагменты, а затем повторить токенизацию каждой части отдельно. например.
/([^{])\s*\{\s*([^}]*?)\s*}/
Затем вы получите селектор и атрибуты в отдельных полях, а затем разделите их. (Даже селектор будет забавно разбираться.) Обратите внимание, что даже это будет иметь боль, если} может появляться внутри кавычек или что-то в этом роде. Вы могли бы, опять же, выкрутить черту из этого, чтобы избежать этого, но, вероятно, даже лучше избегать регулярного выражения здесь и обрабатывать его, анализируя одно поле за раз, возможно, используя парсер с рекурсивным спусканием или yacc/bison или что угодно.
Ответ 2
Вы пытаетесь вытащить структуру из данных, а не только отдельных значений. Регулярные выражения могут быть тягостно растянуты, чтобы выполнить эту работу, но вы действительно входите в парсерную территорию и должны вытаскивать большие пушки, а именно парсеров.
Я никогда не использовал инструменты генерации парсера PHP, но они выглядят хорошо после легкого сканирования документов. Проверьте LexerGenerator и ParserGenerator. LexerGenerator возьмет кучу регулярных выражений, описывающих различные типы токенов на языке (в данном случае CSS), и выплюнет код, который распознает отдельные токены. ParserGenerator возьмет грамматику, описание того, что на языке составлено из чего-то другого, и выплеск парсера, код, который берет кучу токенов и возвращает дерево синтаксиса (структура данных, которую вы после.
Ответ 3
Не используйте собственное регулярное выражение для синтаксического анализа CSS.
Зачем изобретать колесо, пока вас ждет код, готовый к использованию и (надеюсь) без ошибок?
Существуют два общедоступных класса, которые могут анализировать CSS для вас:
Пакет HTML_CSS PEAR на pear.php.net
и
Класс Parser CSS в PHPCLasses:
http://www.phpclasses.org/browse/package/1289.html
Ответ 4
Я бы рекомендовал не использовать регулярное выражение для разбора 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", "");}
(кажется, это отладочная вещь, которая была оставлена)
Я отразил script здесь, с приведенными выше изменениями в
Ответ 5
Я использую регулярное выражение ниже, и это в значительной степени работает... конечно, этот вопрос уже старен, и я вижу, что вы отказались от своих усилий... но в случае, если кто-то еще пробегает по нему:
(?<selector>(?:(?:[^,{]+),?)*?)\{(?:(?<name>[^}:]+):?(?<value>[^};]+);?)*?\}
(hafta сначала удаляет все /* комментарии */ из вашего CSS, чтобы быть в безопасности)
Ответ 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
)
)
Ответ 7
Попробуйте это
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";
Ответ 8
Исходя из текущего ответа Tanktalus, там есть несколько улучшений и красных случаев.
Редактор Parsing CSS
\s*([^{]+)\s*\{\s*([^}]*?)\s*}
Это Regex выполнит некоторую прорисовку пространства и попадет на некоторые дополнительные граничные случаи, перечисленные в этом примере: https://regex101.com/r/qQRIHx/5
ключ: пары значений; Ловушки дальнейшего сложного регулярного выражения
Я тоже начал работать над разграничением пар ключ: значение, но быстро увидел в случае, когда в селекторе было несколько стилей, что вещи начали становиться более хитрыми, чем я хотел. Вы можете просмотреть версию 1 регулярного выражения, где я попытался разграничить значения ключа: и как это произошло с несколькими объявлениями здесь: https://regex101.com/r/qQRIHx/1
Реализация
Как уже упоминалось, вы должны разбить это на несколько шагов для анализа и tokenize вашего css. Это регулярное выражение поможет вам получить объявления, но вам нужно будет их разобрать.
Объявление Parser
Вы можете использовать что-то подобное для анализа объявлений после получения первого набора совпадений.
([^:\s]+)*\s*:\s*([^;]+);
Пример: https://regex101.com/r/py9OKO/1/
Кронштейн
В приведенном выше примере отлично работает с несколькими объявлениями, но возможно, что это всего лишь 1 объявление без конца двоеточия, которое будет отображаться в [большинстве] браузеров, но сломает это регулярное выражение.
Отмеченные случаи
Вам также может потребоваться учитывать вложенные правила в случае, если есть запрос на мультимедиа. В этом случае я попытался бы запустить регулярное выражение css, соответствующее извлеченным объявлениям. Если вы получаете совпадения, вы можете запустить рекурсию (хотя я не уверен, что там, где у вас было бы более 1 уровня, вложенных для ванильного CSS).
Кронштейны
- Это не обрабатывает правильный фигурный скобок в строке