CSS - это очень простой, обычный язык, который означает, что он может быть полностью проанализирован Regex.Все, что нужно сделать, - это группы селекторов, за которыми следует группа параметров, разделенных двоеточиями.
Обратите внимание, что все регулярные выражения в этом посте должны иметь подробный и dotall установлены флаги (/ s и / x в некоторых языках, re.DOTALL и re.VERBOSE в Python).
Чтобы получить пары (селекторы, правила):
\s* # Match any initial space
([^{}]+?) # Ungreedily match a string of characters that are not curly braces.
\s* # Arbitrary spacing again.
\{ # Opening brace.
\s* # Arbitrary spacing again.
(.*?) # Ungreedily match anything any number of times.
\s* # Arbitrary spacing again.
\} # Closing brace.
Это не будет работать в редком случае наличия заключенной в кавычки фигурной скобки в селекторе атрибута (например, img[src~='{abc}']
) или в правиле (например, background: url('images/ab{c}.jpg')
).Это можно исправить, усложнив регулярное выражение:
\s* # Match any initial space
((?: # Start the selectors capture group.
[^{}\"\'] # Any character other than braces or quotes.
| # OR
\" # An opening double quote.
(?:[^\"\\]|\\.)* # Either a neither-quote-not-backslash, or an escaped character.
\" # And a closing double quote.
| # OR
\'(?:[^\']|\\.)*\' # Same as above, but for single quotes.
)+?) # Ungreedily match all that once or more.
\s* # Arbitrary spacing again.
\{ # Opening brace.
\s* # Arbitrary spacing again.
((?:[^{}\"\']|\"(?:[^\"\\]|\\.)*\"|\'(?:[^\'\\]|\\.)*\')*?)
# The above line is the same as the one in the selector capture group.
\s* # Arbitrary spacing again.
\} # Closing brace.
# This will even correctly identify escaped quotes.
Вау, это горстка.Но если вы подойдете к нему модульно, вы заметите, что он не так сложен, как кажется на первый взгляд.
Теперь, чтобы разделить селекторы и правила, мы должны сопоставить строки символов, которые либоне разделители (где разделитель - запятая для селекторов и точка с запятой для правил) или строки в кавычках с чем-либо внутри.Мы будем использовать тот же шаблон, который мы использовали выше.
Для селекторов:
\s* # Match any initial space
((?: # Start the selectors capture group.
[^,\"\'] # Any character other than commas or quotes.
| # OR
\" # An opening double quote.
(?:[^\"\\]|\\.)* # Either a neither-quote-not-backslash, or an escaped character.
\" # And a closing double quote.
| # OR
\'(?:[^\'\\]|\\.)*\' # Same as above, but for single quotes.
)+?) # Ungreedily match all that.
\s* # Arbitrary spacing.
(?:,|$) # Followed by a comma or the end of a string.
Для правил:
\s* # Match any initial space
((?: # Start the selectors capture group.
[^,\"\'] # Any character other than commas or quotes.
| # OR
\" # An opening double quote.
(?:[^\"\\]|\\.)* # Either a neither-quote-not-backslash, or an escaped character.
\" # And a closing double quote.
| # OR
\'(?:[^\'\\]|\\.)*\' # Same as above, but for single quotes.
)+?) # Ungreedily match all that.
\s* # Arbitrary spacing.
(?:;|$) # Followed by a semicolon or the end of a string.
Наконец, для каждого правила мы можем разделить(один раз!) на двоеточие для получения пары свойство-значение.
Объединение всего этого в программу Python (регулярные выражения такие же, как и выше, но не подробные для экономии места):
import re
CSS_FILENAME = 'C:/Users/Max/frame.css'
RE_BLOCK = re.compile(r'\s*((?:[^{}"\'\\]|\"(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)*\')+?)\s*\{\s*((?:[^{}"\'\\]|"(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)*\')*?)\s*\}', re.DOTALL)
RE_SELECTOR = re.compile(r'\s*((?:[^,"\'\\]|\"(?:[^"\\]|\\.)*\"|\'(?:[^\'\\]|\\.)*\')+?)\s*(?:,|$)', re.DOTALL)
RE_RULE = re.compile(r'\s*((?:[^;"\'\\]|\"(?:[^"\\]|\\.)*\"|\'(?:[^\'\\]|\\.)*\')+?)\s*(?:;|$)', re.DOTALL)
css = open(CSS_FILENAME).read()
print [(RE_SELECTOR.findall(i),
[re.split('\s*:\s*', k, 1)
for k in RE_RULE.findall(j)])
for i, j in RE_BLOCK.findall(css)]
Для этого примера CSS:
body, p#abc, #cde, a img .fgh, * {
font-size: normal; background-color: white !important;
-webkit-box-shadow: none
}
#test[src~='{a\'bc}'], .tester {
-webkit-transition: opacity 0.35s linear;
background: white !important url("abc\"cd'{e}.jpg");
border-radius: 20px;
opacity: 0;
-webkit-box-shadow: rgba(0, 0, 0, 0.6) 0px 0px 18px;
}
span {display: block;} .nothing{}
... мы получаем (с интервалом для ясности):
[(['body',
'p#abc',
'#cde',
'a img .fgh',
'*'],
[['font-size', 'normal'],
['background-color', 'white !important'],
['-webkit-box-shadow', 'none']]),
(["#test[src~='{a\\'bc}']",
'.tester'],
[['-webkit-transition', 'opacity 0.35s linear'],
['background', 'white !important url("abc\\"cd\'{e}.jpg")'],
['border-radius', '20px'],
['opacity', '0'],
['-webkit-box-shadow', 'rgba(0, 0, 0, 0.6) 0px 0px 18px']]),
(['span'],
[['display', 'block']]),
(['.nothing'],
[])]
Простое упражнение для читателя: напишите регулярное выражение дляудалить комментарии CSS (/* ... */
).