Подтвердить что ты не робот

Минимизировать/сжать CSS с регулярным выражением?

В PHP вы можете сжимать/уменьшать CSS с помощью регулярного выражения (PCRE)?

(Как теоретически в regex. Я уверен, что есть библиотеки, которые делают это хорошо.)

Фоновая заметка: потратив часы на ответ на вопрос deleted (half crap), я подумал, что отправлю часть основного вопроса и ответьте мне. Надеюсь, что все в порядке.

4b9b3361

Ответ 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.

Ответ 2

Здесь немного измененная версия @Qtax answer, которая устраняет проблемы с calc() благодаря альтернативному регулярному выражению из @matthiasmullie Minify library.

function minify_css( $string = '' ) {
    $comments = <<<'EOS'
(?sx)
    # don't change anything inside of quotes
    ( "(?:[^"\\]++|\\.)*+" | '(?:[^'\\]++|\\.)*+' )
|
    # comments
    /\* (?> .*? \*/ )
EOS;

    $everything_else = <<<'EOS'
(?six)
    # don't change anything inside of quotes
    ( "(?:[^"\\]++|\\.)*+" | '(?:[^'\\]++|\\.)*+' )
|
    # spaces before and after ; and }
    \s*+ ; \s*+ ( } ) \s*+
|
    # all spaces around meta chars/operators (excluding + and -)
    \s*+ ( [*$~^|]?+= | [{};,>~] | !important\b ) \s*+
|
    # all spaces around + and - (in selectors only!)
    \s*([+-])\s*(?=[^}]*{)
|
    # spaces right of ( [ :
    ( [[(:] ) \s++
|
    # spaces left of ) ]
    \s++ ( [])] )
|
    # spaces left (and right) of : (but not in selectors)!
    \s+(:)(?![^\}]*\{)
|
    # spaces at beginning/end of string
    ^ \s++ | \s++ \z
|
    # double spaces to single
    (\s)\s+
EOS;

    $search_patterns  = array( "%{$comments}%", "%{$everything_else}%" );
    $replace_patterns = array( '$1', '$1$2$3$4$5$6$7' );

    return preg_replace( $search_patterns, $replace_patterns, $string );
}

Ответ 3

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

На самом деле '//комментарии' недопустимы в css.

ob_start('ob_handler');
if(!file_exists('style/style-min.css) 
or filemtime('style/style.css') > filemtime('style/style-min.css')){
  $css=file_get_contents('style/style.css');    
  //you need to escape some more charactes if pattern is an external string.
  $from=array('@\\s*/\\*.*\\*/\\s*@sU', '/\\s{2,}/');
  $to=  array(''                      , ' ');
  $css=preg_replace($from,$to,$css); 
  $css=preg_replace('@\s*([\:;,."\'{}()])\s*@',"$1",$css);  
  $css=preg_replace('@;}@','}',$css);
  header('Content-type: text/css');
  echo $css;
  file_put_contents('style/style-min.css',$css);
  //etag- modified- cache-control- header
  }
else{
  //exit if not modified?
  //etag- modified- cache-control- header
  header('Content-type: text/css');
  readfile('style/style-min.css');
  }   
ob_end_flush();

PS Кто дал мне минус, прежде чем я буду готов с набрав? QTax. В течение короткого времени я забыл избежать обратных косых черт в массиве $fom. PSS. Только новая версия PHP undestand параметр 'U', который делает регулярное выражение неровным.