简单的正则表达式 CSS 压缩器/压缩器
(好吧,这可能不是太简单,但非常简单。)
要求
此答案假设要求是:
- 删除 cmets
- 用单个空格替换超过 1 个空格的空格组合
- 删除元字符周围的所有空格:
{、}、;、,、>、~、+、-
- 删除
!important 周围的空格
- 删除
: 周围的空格,选择器除外(您必须在其前面保留一个空格)
- 删除运算符周围的空格,例如
$=
- 删除
(/[右侧和)/]左侧的所有空格
- 删除字符串开头和结尾的所有空格
- 删除块中的最后一个
;
- 不要更改字符串中的任何内容
- 不必处理无效的 CSS
请注意,此处的要求不包括将 CSS 属性转换为更短的版本(例如使用速记属性而不是几个全长属性,在不需要的地方删除引号)。
这是正则表达式一般无法解决的问题。
解决方案
分两遍更容易解决这个问题:首先删除 cmets,然后是其他所有内容。
应该可以一次性完成,但是您必须将所有 \s 替换为同时匹配空格和 cmets 的表达式(以及其他一些修改)。
去除cmets的第一遍表达式:
(?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);
}
快速测试
可以找到at 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 与这个正则表达式缩小器相比有一些缺点:
- 它会留下部分 cmets。 (这可能是有原因的。也许是一些 CSS hack。)
- 它不会删除某些表达式中运算符周围的空格
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 不会删除某些选择器中的 cmets 或空格。但它确实缩小为速记属性。后者可能有助于更多地压缩普通 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 压缩器)应该在这方面做得更好,并且比这个正则表达式压缩器给出更短的结果。