Вопрос по css, regex, minify, php, pcre – Сократить / сжать CSS с помощью регулярных выражений?

23

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

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

Справочная информация: после нескольких часов написания ответа наудаленный (наполовину дерьмовый) вопрос, Я думал я'Я оставлю часть основного вопроса и сам отвечу на него. Надеюсь этохорошо

@SamuelLiew, этот немного более ограничен: «Я»Я только пытаюсь удалить любые разрывы строк / вкладки и комментарии из файла " Qtax
Вот'Еще один вопрос для рассмотрения:stackoverflow.com/questions/1379277/... Samuel Liew
Неверная ссылка, та, что в приведенном выше комментарии, является правильной. Samuel Liew
+1 за вопрос с ответом :) user1646111

Ваш Ответ

3   ответа
-1

как я это делаю. Со сжатием. А ты нене нужно заботиться, если вы что-то изменили в источнике.

По факту '//Комментарии' не допускаются в 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 понимает "U" param, который делает регулярное выражение несвязным.

Несколько проблем с этим. Попробуйте это на моем примере CSS, чтобы убедиться в этом. Это неудалить комментарии правильно несколькими способами. Пример CSS, который нарушает этот код:,.content: " foo /* bar */ baz";/* comment */ color: #f00; /* comment */ Qtax
4

Вот'Слегка измененная версия@Qtax»ответ которая решает проблемы сcalc() благодаря альтернативному регулярному выражению из@matthiasmullie»s Сократить библиотеку.

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', '(Как теоретическое в регулярном выражении. Я '2Справочная информация: после нескольких часов написания ответа на4, Я думал я'6хорошо8' );

    return preg_replace( $search_patterns, $replace_patterns, $string );
}
44
Простой regex 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+

Заменено на.(Как теоретическое в регулярном выражении. Я '2Справочная информация: после нескольких часов написания ответа на4, Я думал я'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я уверен, что есть библиотеки, которые делают это хорошо.3удаленный (наполовину дерьмовый вопрос5Я оставлю часть основного вопроса и сам отвечу на него. Надеюсь это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 байт длиннее, чем выходные данные минификатора регулярного выражения выше.

cssminifier.com имеет некоторые недостатки по сравнению с этим ограничителем регулярных выражений:

Он оставляет части комментариев. (Может быть причина для этого. Может быть, некоторые 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 байт длиннее, чем вывод минификатора регулярных выражений.

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

Бок о бок сравнение

Сокращенный вывод от разных минифайеров для того же ввода, что и в приведенном выше примере. (Оставшиеся разрывы строк заменены пробелами.

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, вероятно, лучше, поскольку он преобразуется в сокращенные свойства.

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

Я перевел ваш код на Python - SRE и использовал его в своем CSS-препроцессоре. Источники здесь:github.com/wyderkat/css-on-diet , Спасибо, что поделились своим кодом. Кстати, по умолчанию движок Python RE, который смотрит вперед, убивает процессор. Tomek Wyderka
Опубликовать как вики === Реп завидую. Wesley Murch
В моем css есть переменные php, на случай, если кто-то другой тоже сделает это, я изменил 2 строки для кавычек на эти 3 условия: <\? [\ s \ S] +? \?> | '(<?\? [\ S \ S] +? \?>| [^»\] ++ | \\.) * + '| "(<?\? [\ S \ S] +? \?>| [^»\] ++ |. \\) * +» Redzarf
Прочитав статью, я чувствую, что вы правы. Это нормально в соответствии с рекомендациями по переполнению стека. kuldeep.kamboj

Похожие вопросы