Ответ 1
Простое регулярное выражение CSS minifier/компрессор
(Хорошо, это может быть не слишком просто, но довольно прямолинейно.)
Требования
Этот ответ предполагает, что требования:
- Удалить комментарии
- Заменить комбинации пробелов длиннее 1 пробела с одним пробелом
- Удалите все пробелы вокруг метасимволов:
{
,}
,;
,,
,>
,~
,+
,-
- Удалите пробелы вокруг
!important
- Удалите пробелы вокруг
:
, за исключением селекторов (где вам нужно оставить пробел перед ним) - Удалите пробелы вокруг таких операторов, как
$=
- Удалите все пробелы справа от
(
/[
и слева от)
/]
- Удалить все пробелы в начале и конце строки
- Удалите последний
;
в блоке - Ничего не меняйте в строках
- Не нужно работать с недопустимым CSS
Обратите внимание, что требования здесь не включают преобразование свойств CSS в более короткие версии (например, использование сокращенных свойств вместо нескольких свойств полной длины, удаление котировок там, где это не требуется). Это то, что регулярное выражение не сможет решить в целом.
Решение
Это проще решить за два прохода: сначала удалите комментарии, затем все остальное.
Это нужно сделать за один проход, но тогда вы должны заменить все \s
выражением, которое соответствует как пробелам, так и комментариям (среди некоторых других модификаций).
Первое выражение для удаления комментариев:
(?xs)
# quotes
(
"(?:[^"\\]++|\\.)*+"
| '(?:[^'\\]++|\\.)*+'
)
|
# comments
/\* (?> .*? \*/ )
Замените $1
.
И удалить все остальное, что вы можете использовать:
(?six)
# quotes
(
"(?:[^"\\]++|\\.)*+"
| '(?:[^'\\]++|\\.)*+'
)
|
# ; before } (and the spaces after it while we're here)
\s*+ ; \s*+ ( } ) \s*+
|
# all spaces around meta chars/operators
\s*+ ( [*$~^|]?+= | [{};,>~+-] | !important\b ) \s*+
|
# spaces right of ( [ :
( [[(:] ) \s++
|
# spaces left of ) ]
\s++ ( [])] )
|
# spaces left (and right) of :
\s++ ( : ) \s*+
# but not in selectors: not followed by a {
(?!
(?>
[^{}"']++
| "(?:[^"\\]++|\\.)*+"
| '(?:[^'\\]++|\\.)*+'
)*+
{
)
|
# spaces at beginning/end of string
^ \s++ | \s++ \z
|
# double spaces to single
(\s)\s+
Заменяется $1$2$3$4$5$6$7
.
Проверка селектора для удаления пробелов перед :
(отрицательный просмотр) может замедлить это по сравнению с соответствующими анализаторами.
Парсеры уже знают, находятся ли они в селекторе или нет, и не нужно выполнять дополнительные проверки, чтобы проверить это.
Пример реализации в PHP
function minify_css($str){
# remove comments first (simplifies the other regex)
$re1 = <<<'EOS'
(?sx)
# quotes
(
"(?:[^"\\]++|\\.)*+"
| '(?:[^'\\]++|\\.)*+'
)
|
# comments
/\* (?> .*? \*/ )
EOS;
$re2 = <<<'EOS'
(?six)
# quotes
(
"(?:[^"\\]++|\\.)*+"
| '(?:[^'\\]++|\\.)*+'
)
|
# ; before } (and the spaces after it while we're here)
\s*+ ; \s*+ ( } ) \s*+
|
# all spaces around meta chars/operators
\s*+ ( [*$~^|]?+= | [{};,>~+-] | !important\b ) \s*+
|
# spaces right of ( [ :
( [[(:] ) \s++
|
# spaces left of ) ]
\s++ ( [])] )
|
# spaces left (and right) of :
\s++ ( : ) \s*+
# but not in selectors: not followed by a {
(?!
(?>
[^{}"']++
| "(?:[^"\\]++|\\.)*+"
| '(?:[^'\\]++|\\.)*+'
)*+
{
)
|
# spaces at beginning/end of string
^ \s++ | \s++ \z
|
# double spaces to single
(\s)\s+
EOS;
$str = preg_replace("%$re1%", '$1', $str);
return preg_replace("%$re2%", '$1$2$3$4$5$6$7', $str);
}
Быстрый тест
Вы можете найти на ideone.com:
$in = <<<'EOS'
p * i , html
/* remove spaces */
/* " comments have no escapes \*/
body/* keep */ /* space */p,
p [ remove ~= " spaces " ] :nth-child( 3 + 2n ) > b span i , div::after
{
/* comment */
background : url( " /* string */ " ) blue !important ;
content : " escapes \" allowed \\" ;
width: calc( 100% - 3em + 5px ) ;
margin-top : 0;
margin-bottom : 0;
margin-left : 10px;
margin-right : 10px;
}
EOS;
$out = minify_css($in);
echo "input:\n";
var_dump($in);
echo "\n\n";
echo "output:\n";
var_dump($out);
Вывод:
input:
string(435) "
p * i , html
/* remove spaces */
/* " comments have no escapes \*/
body/* keep */ /* space */p,
p [ remove ~= " spaces " ] :nth-child( 3 + 2n ) > b span i , div::after
{
/* comment */
background : url( " /* string */ " ) blue !important ;
content : " escapes \" allowed \\" ;
width: calc( 100% - 3em + 5px ) ;
margin-top : 0;
margin-bottom : 0;
margin-left : 10px;
margin-right : 10px;
}
"
output:
string(251) "p * i,html body p,p [remove~=" spaces "] :nth-child(3+2n)>b span i,div::after{background:url(" /* string */ ") blue!important;content:" escapes \" allowed \\";width:calc(100%-3em+5px);margin-top:0;margin-bottom:0;margin-left:10px;margin-right:10px}"
Сравниваемые
cssminifier.com
Результаты cssminifier.com для того же ввода, что и выше:
p * i,html /*\*/body/**/p,p [remove ~= " spaces "] :nth-child(3+2n)>b span i,div::after{background:url(" /* string */ ") blue;content:" escapes \" allowed \\";width:calc(100% - 3em+5px);margin-top:0;margin-bottom:0;margin-left:10px;margin-right:10px}
Длина 263 байт. 12 байта длиннее выходного выражения regex minifier выше.
cssminifier.com имеет некоторые недостатки по сравнению с этим регулярным выражением minifier:
- Он оставляет части комментариев. (Может быть, причина для этого. Возможно, некоторые хаки CSS.)
- Он не удаляет пробелы вокруг операторов в некоторых выражениях
CSSTidy
Вывод CSSTidy 1.3 (через codebeautifier.com) с самым высоким уровнем сжатия:
p * i,html /* remove spaces */
/* " comments have no escapes \*/
body/* keep */ /* space */p,p [ remove ~= " spaces " ] :nth-child( 3 + 2n ) > b span i,div::after{background:url(" /* string */ ") blue!important;content:" escapes \" allowed \\";width:calc(100%-3em+5px);margin:0 10px;}
Длина 286 байт. 35 байт дольше, чем выход регулярного выражения minifier.
CSSTidy не удаляет комментарии или пробелы в некоторых селекторах. Но он минимизирует стенографические свойства. Последнее, вероятно, должно помочь сжать обычный CSS намного больше.
Сравнение бок о бок
Минимизированный вывод из другого minifiers для того же входа, что и в приведенном выше примере. (Остатки оставшейся замены заменяются пробелами.)
this answern (251): p * i,html body p,p [remove~=" spaces "] :nth-child(3+2n)>b span i,div::after{background:url(" /* string */ ") blue!important;content:" escapes \" allowed \\";width:calc(100%-3em+5px);margin-top:0;margin-bottom:0;margin-left:10px;margin-right:10px}
cssminifier.com (263): p * i,html /*\*/body/**/p,p [remove ~= " spaces "] :nth-child(3+2n)>b span i,div::after{background:url(" /* string */ ") blue!important;content:" escapes \" allowed \\";width:calc(100% - 3em+5px);margin-top:0;margin-bottom:0;margin-left:10px;margin-right:10px}
CSSTidy 1.3 (286): p * i,html /* remove spaces */ /* " comments have no escapes \*/ body/* keep */ /* space */p,p [ remove ~= " spaces " ] :nth-child( 3 + 2n ) > b span i,div::after{background:url(" /* string */ ") blue!important;content:" escapes \" allowed \\";width:calc(100%-3em+5px);margin:0 10px;}
Для обычного CSS CSSTidy, вероятно, лучше всего, поскольку он преобразуется в сокращенные свойства.
Я предполагаю, что существуют другие minifiers (такие как компрессор YUI), которые должны быть лучше при этом, и дают более короткий результат, чем это регулярное выражение minifier.