[翻译] 做CSS精简时可能会用到的正则表达式

原文:http://regexadvice.com/blogs/mash/archive/2008/03/27/Additional-CSS-minifying-regex-patterns.aspx

注意:本文提到的正则表达式都使用了IgnoreCase=true选项。

我观察了YUI Compressor中用于精简CSS的正则表达式,并给出了一些我认为能有助于这一工作的其他正则表达式。

我所看到的代码已经能够通过简单的字符串替换修剪掉“上-右-下-左”值中不必要的零值。但这是通过三个独立的替换操作完成的。如果能用正则表达式处理这种情况,代码将会变得更简单。

(伪代码)

string.Replace(":0 0 0 0;","0;")

string.Replace(":0 0 0;","0;")

string.Replace(":0 0;","0;")

可以变为:

Regex.Replace(input,":("s*0)("s+0){0,3}"s*;",":0;")

够简单了吧。但我想,怎么能就此停滞不前呢?于是我给出了可以处理所有数值的正则表达式:

:"s*(0|(?:(?:"d*".?"d+(?:p(?:[xct])|(?:[cem])m|%|in|ex))))("s+"1){1,3};

(【译注】测试地址:http://regex-lib.net/Regex/Test/?id=d090db4eaf3b41e2b4e75290ad5fe670

用于进行替换的字符串很简单:“:$1”。

这一步完成后,下面就是处理各个数值并不全为0的情形。

如果您不大了解CSS,这里简单说一下,此处可能出现4个值:

1)如果只指定一个值,则另外三个值也都隐式地指定为相同的值(X = X X X);

2)如果指定了二个值,则第三个值和第一个相等,第四个值和第二个相等(X Y = X Y X Y);

3)如果指定了三个值,则第四个值等于第二个值(X Y Z = X Y Z Y)。

当然,在精简时您肯定希望用更短的语法。下面的正则表达式可以使其变短。所有这些情况使用的替换字符串都和前面一样,是“:$1”。

将4个参数替换为2个(X Y X Y变为X Y)

:"s*((0|(?:(?:"d?".?"d(?:p(?:[xct])|(?:[cem])m|%|in|ex))))"s+(0|(?:(?:"d?".?"d(?:p(?:[xct])|(?:[cem])m|%|in|ex)))))"s+"2"s+"3;

(【译注】测试地址:http://regex-lib.net/Regex/Test/?id=e47bbb7741bd438e9fd37cae7a9d4a24

4个变为3个(X Y Z Y变为X Y Z)或3个变为2个(X Y X变为X Y)

:"s*((?:(?:0|(?:(?:"d?".?"d(?:p(?:[xct])|(?:[cem])m|%|in|ex))))"s+)?(0|(?:(?:"d?".?"d(?:p(?:[xct])|(?:[cem])m|%|in|ex))))"s+(?:0|(?:(?:"d?".?"d(?:p(?:[xct])|(?:[cem])m|%|in|ex)))))"s+"2;

(【译注】测试地址:http://regex-lib.net/Regex/Test/?id=6bddaab88e724f68a5808d352cc556ca

较长的这个正则表达式只不过是重复了其中一个子模式而已。

基于以上这些正则表达式,我提出了类似的模式,用来处理border-style、outline-style、border-color和outline-color。

border-style/outline-style

替换字符串为“$1-style:$2;”。

(outline|border)-style"s*:"s*(none|hidden|d(?:otted|ashed|ouble)|solid|groove|ridge|inset|outset )(?:"s+"2){1,3};

(【译注】测试地址:http://regex-lib.net/Regex/Test/?id=e46542e4f8bb48429a9f5c9b7fdc546b

(outline|border)-style"s*:"s*((none|hidden|d(?:otted|ashed|ouble)|solid|groove|ridge|inset|outset )"s+(none|hidden|d(?:otted|ashed|ouble)|solid|groove|ridge|inset|outset ))(?:"s+"3)(?:"s+"4);

(【译注】测试地址:http://regex-lib.net/Regex/Test/?id=c5e39786b75143588049042aea0dcd41

(outline|border)-style"s*:"s*((?:(?:none|hidden|d(?:otted|ashed|ouble)|solid|groove|ridge|inset|outset )"s+)?(none|hidden|d(?:otted|ashed|ouble)|solid|groove|ridge|inset|outset )"s+(?:none|hidden|d(?:otted|ashed|ouble)|solid|groove|ridge|inset|outset ))(?:"s+"3);

(【译注】测试地址:http://regex-lib.net/Regex/Test/?id=6558eb133f26435cba770aa1c69f91dd

border-color/outline-color

替换字符串为“$1-color:$2;”。

(outline|border)-color"s*:"s*((?:"#(?:[0-9A-F]{3}){1,2})|"S+)(?:"s+"2){1,3};

(【译注】测试地址:http://regex-lib.net/Regex/Test/?id=05ec5037fcf843609e14c821b7f76034

(outline|border)-color"s*:"s*(((?:"#(?:[0-9A-F]{3}){1,2})|"S+)"s+((?:"#(?:[0-9A-F]{3}){1,2})|"S+))(?:"s+"3)(?:"s+"4);

(【译注】测试地址:http://regex-lib.net/Regex/Test/?id=4a4c4e9dcdf74a97ba3daa113e56a9ca

(outline|border)-color"s*:"s*((?:(?:(?:"#(?:[0-9A-F]{3}){1,2})|"S+)"s+)?((?:"#(?:[0-9A-F]{3}){1,2})|"S+)"s+(?:(?:"#(?:[0-9A-F]{3}){1,2})|"S+))(?:"s+"3);

(【译注】测试地址:http://regex-lib.net/Regex/Test/?id=82fedb7249b041f3a13e5552fc626b47

我还给出了其他一些用于替换代码的正则表达式,但由于它们都使用了反相匹配,所以移植性会有问题。

这个模式:

"s+((?:[!{};>+()"],])|(?<={[^{}]*):(?=[^}]*}))

(【译注】测试地址:http://regex-lib.net/Regex/Test/?id=928254009ef849cfb565022b6489355e

用于匹配字符前面不必要的空白字符。要小心伪选择器和伪类前面的冒号。替换字符串为“$1”。

这个模式:

(?<!["x22"x27=]"s*)"#([0-9A-F])"1([0-9A-F])"2([0-9A-F])"3

(【译注】测试地址:http://regex-lib.net/Regex/Test/?id=1e210252d62d4cd2a9f0cb9f2513aeb8

用于精简16进制颜色值,将AABBCC变为ABC。替换字符串为“$1$2$3”。

我还写了一个用于匹配形如“rgb(x,y,z)”的RGB值的正则表达式,其中的x、y、z都在0-255之间。

rgb"s*"x28((?:25[0-5])|(?:2[0-4]"d)|(?:[01]?"d?"d))"s*,"s*((?:25[0-5])|(?:2[0-4]"d)|(?:[01]?"d?"d))"s*,"s*((?:25[0-5])|(?:2[0-4]"d)|(?:[01]?"d?"d))"s*"x29

(【译注】测试地址:http://regex-lib.net/Regex/Test/?id=c616455c751647ff9c1a430f4f0cfb96

这个模式仅用于匹配现有代码,并将每个数值放到一个组中,以便进行进一步处理。可以使用matchevaluator遍历每一个匹配并完成10进制到16进制的转换。

我自己进行了一些alpha测试,但我写的css根本不能用作测试材料,所以干脆给大家进行beta测试。我并不是很懂CSS hack,因此我不知道这些精简会不会对CSS hack产生负面影响。我还会将这些信息告诉YUI Compressor的维护者,看看他们能不能采纳。