IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

[正则优化] CSS属性选择符的匹配

Miller 2011-09-04 23:33:04 累计浏览 3,739 次
本机暂存

正则表达式如下

\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]

这个正则表达式的作用是用来匹配CSS属性选择符,例如: 

/*能匹配成功的*/
[class='abcdefg-abcdefg']
  
/*匹配失败的*/
[class='abcdefg-abcdefg'

成功匹配的过程如下:

2010-11-06_134439

短短的文本却需要匹配451步,效率是比较低的,总结一下大概有以下几个问题:

1. ((?:[\w\u00c0-\uFFFF-]|\\.)+) 这一部分表达式是用来匹配属性名称的,+号本身是贪婪匹配的,但是限定在一个分组之后就变成循环了,括号中的表达式会一次次的循环执行,因此效率也就比较低了。

2. (\S?=)部分是用来匹配属性关系符的,例如"="、"~="。当匹配的属性关系符的长度是2时,该表达式并没有问题,\S?能够成功的匹配第一个关系符,例如"~="中的"~"。但是在匹配"="这样只有一个字符的关系符时,\S?= 首先会用\S?来匹配"=",而且这个匹配是成功的,之后会再用表达式中的等号继续匹配,但匹配时失败的,这时候需要增加一次回溯来保证匹配成功,因此这里的回溯也是可以优化的。

3. 在匹配属性值时,使用了一个懒惰型的表达式,即(.*?),这也是按字符循环,速度较慢,可以进行优化。

根据以上的问题相应的做出了以下的改进:

1. ((?:[\w\u00c0-\uFFFF-]|\\.)+)  这一部分之所以要使用分组是因为属性名中可以包含\.这样的字符,在此可以将它视为特殊字符,因此使用(open normal(special normal+)* close)的格式对这部分进行重构,结果为:[\w\u00c0-\uFFFF-]*(\\.[\w\u00c0-\uFFFF-]*)*,这可以以最大的步伐匹配字符。

2. (\S?=)部分需要排除\S?对"="进行匹配的可能性,因此修改为 [^\s=]?

3. (['"]*)(.*?)\3|)匹配属性值的时候之所以使用懒惰匹配是因为使用了反向引用,这里可以使用分组来替换。

根据以上几点做出的修改后,结果如下:

\[\s*((?:[\w\u00c0-\uFFFF-]*(?:\\.[\w\u00c0-\uFFFF-]*)*))\s*(?:([^\s=]?=)
\s*('[^'\s\]]*'|"[^"\s\]]*"|[^'"\s\]]*))\s*\]

使用优化后的表达式匹配上文相同的文本得到的结果如下:

2010-11-06_174807

由原来的451步缩减为21步,是一个相当大的提升,这里匹配属性名和属性值时都是最大步伐的消耗掉文本,而且匹配关系符时不再有回溯。

而实际上浏览器的测试结果也验证了这一点:http://jsperf.com/regexp-test-1

结果显示,在匹配成功的情况下,IE、Firefox的性能提升在1倍以上,Chrome下也超过50%。

而在匹配失败的情况下,优化后的表达式也是回溯次数最少的,性能提升在以上case中也有体现,IE和Firefox下的提升高达4-5倍,Chrome下提升2倍左右。

同分类推荐文章

  1. translateZ() (2026-06-25 21:18:56)
  2. translateY() (2026-06-25 21:17:56)
  3. translateX() (2026-06-25 21:16:01)

查看更多 前端 文章 →

建议继续学习

  1. 50个活力和动感的网页设计-颜色的灵感 (累计阅读 34,442)
  2. WEB系统需要关注的一些点 (累计阅读 18,220)
  3. 视觉设计前瞻实用性研究(PNVD) 第二期 (累计阅读 12,977)
  4. 各公司对前端开发的职位描述 (累计阅读 10,405)
  5. iframe大小自适应 (累计阅读 10,057)
  6. 大并发下的高性能编程 – 改进的(用户态)自旋锁 (累计阅读 9,044)
  7. 低成本和高性能MySQL云数据的架构探索 (累计阅读 8,585)
  8. 浏览器的渲染原理简介 (累计阅读 8,377)
  9. 解决IE6从Nginx服务器下载图片不Cache的Bug (累计阅读 8,357)
  10. 程序员眼里IE浏览器是什么样的 (累计阅读 8,011)