Logo

never-online

A crisis is a terrible thing to waste.
  • Blog首页
  • 推荐日志
  • 关于我
  • 留言簿
  • 设计
  • 订阅RSS
  • 登录
« 构建通用UI控件的那...
radio list的细节 »
分类: Web Dev
推荐日志

从trim原型函数看js正则表达式的性能

[ 2008-11-24 20:20:21 | 作者: Rank ]
字体大小: 大 | 中 | 小
Close Advertisement
see also:firefox 2 的正则表达式细节

如果你看到别人写trim函数是用循环而不用正则表达式来写,请不要取笑,也许,他们就是高手。如果你很自信你的trim函数效率很高,请看完本文再下结论。

一般情况下用正则写法为:
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
<script type="text/javascript">//<![CDATA[
  String.prototype.trim = function () {
    return this.replace(/^[\s\t ]+|[\s\t ]+$/g, '');
  }
  s = ' never-online\'s weblog, www.never-online.net/blog ';
  alert(s.trim().length);
//]]></script>

如果遇到大数据的变长字符串的话就会发现这个是很耗资源的。效率并不高,有的时候甚至无法忍受。
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <title></title>
 <meta http-equiv="Pragma" content="no-cache" />
 <meta http-equiv="Cache-Control" content="no-cache" />
 <meta http-equiv="Expires" content="0" />
 <meta http-equiv="ImageToolbar" content="no" />
 <style type="text/css" title="default" media="screen">
 /*<![CDATA[*/

 /*]]>*/
 </style>
 </head>
  <body>
  <textarea>请在这里写足够多的空格或者tab字符。</textarea>
  <script type="text/javascript">//<![CDATA[
  String.prototype.trim = function () {
    return this.replace(/^[\s\t ]+|[\s\t ]+$/g, '');
  }
  var s = document.getElementsByTagName('textarea')[0].value
  var d = new Date();
  s.trim();
  alert(new Date()-d);
//]]></script>
  </body>
</html>

在解释这个原因的时候想起以前看到master regular expression里面有提到过。NFA和DFA的引擎是有区别的。js/perl/php/java/.net都是NFA引擎。
而DFA与NFA机制上的不同带来5个影响:
1. DFA对于文本串里的每一个字符只需扫描一次,比较快,但特性较少;NFA要翻来覆去吃字符、吐字符,速度慢,但是特性丰富,所以反而应用广泛,当今主要的正则表达式引擎,如Perl、Ruby、Python的re模块、Java和.NET的regex库,都是NFA的。
2. 只有NFA才支持lazy和backreference(后向引用)等特性;
3. NFA急于邀功请赏,所以最左子正则式优先匹配成功,因此偶尔会错过最佳匹配结果;DFA则是“最长的左子正则式优先匹配成功”。
4. NFA缺省采用greedy量词(就是对于/.*/、/\w+/这样的“重复n”次的模式,以贪婪方式进行,尽可能匹配更多字符,直到不得以罢手为止),NFA会优先匹配量词。
5. NFA可能会陷入递归调用的陷阱而表现得性能极差。

backtracking(回朔)
当NFA发现自己吃多了,一个一个往回吐,边吐边找匹配,这个过程叫做backtracking。由于存在这个过程,在NFA匹配过程中,特别是在编写不合理的正则式匹配过程中,文本被反复扫描,效率损失是不小的。明白这个道理,对于写出高效的正则表达式很有帮助。

定位/分析原因
在解释上面的trim原型方法的时候。经过测试,先不说结果是否正确,有几个方法是可以化解JS NFA引擎的回朔次数的
a. 去掉限定的量词,即改成
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
 String.prototype.trim = function () {
    return this.replace(/^[\s\t ]+|[\s\t ]$/g, '');
 }
b. 去掉字符串尾匹配。即改成:
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
 String.prototype.trim = function () {
    return this.replace(/^[\s\t ]+/g, '');
 }
c.加入多行匹配。即改成:
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
 String.prototype.trim = function () {
    return this.replace(/^[\s\t ]+|[\s\t ]+$/mg, '');
 }

从以上三种改法结合文中开头的NFA资料,我们可以大概的知道trim性能出现问题的原因
  • 量词限定将优先匹配。
  • 量词限定在结尾可能会使JS的正则引擎不停的回朔,出现递归的一个陷阱,这个递归的深度太深。如果字符串更大一点应该会出现栈溢出了。
  • 多行既然能够匹配,而且性能消耗不大。性能上没有任何问题,从一个写这个正则程序的人角度上去看,多行明显比单行要替换的空串多得多。所以第二点的结论应该是对的
改良
首先确定匹配字符串的开始正则是没有任何效率问题的。而匹配结束的时候会出现性能问题,那可以采用正则与传统相结合来改善这个trim性能问题。

例如:
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
<script type="text/javascript">//<![CDATA[
  String.prototype.trim = function () {
    var s = this.replace(/^[\s\t ]+/g, '');
    从s后端开始查找,并回循环到最后一个非空字符串,代码略。
  }
//]]></script>

see also:firefox 2 的正则表达式细节
[最后修改由 Rank, 于 2008-12-09 00:13:43]
评论Feed 评论Feed: http://www.never-online.net/blog/feed.asp?q=comment&id=259

浏览模式: 显示全部 | 评论: 6 | 引用: 0 | 排序 | 浏览: 4015
引用 星辉一冷*
[ 2008-11-24 21:13:30 ]
嗯, 最近也刚刚发现这个...
引用 闲耘*
[ 2008-12-22 13:00:41 ]
深入浅出,研究的很好。
不过有没有效率对比的数据呢?前后都使用循环或其他方法,效率比之如何呢?
引用 闲耘*
[ 2008-12-22 13:01:26 ]
怎么默认是悄悄话的,崩溃。
引用 Rank
[ 2008-12-22 14:48:40 ]
@闲耘:效率对比是差一个数量级的,我用长字符串做测试后,长字符串的长度未统计,单纯用正则的速度如果是3~5s的话,用正则结合传统的方式大概是不到100ms。

至于默认是悄悄话是因为现在国内的形势(gov)是要把发言的内容都要进行审核。。。。被逼无奈而为之~~~
[最后修改由 Rank, 于 2008-12-22 14:50:49]
引用 闲耘*
[ 2008-12-23 21:15:45 ]
我测试了一下,两头都纯循环(使用substring)的方式,
比头部使用正则(replace),尾部使用循环(然后substring)的方式,
效率还高3倍左右(大文本,不同型号的比率大概也不同),
应该是replace效率较低的缘故。
如果能实现快速找到第一个非空白和最后一个非空白的位置,效率就更高了,可惜我的智慧还达不到。
p.s. 貌似你的安全规则是hard code..
引用 Rank
[ 2008-12-31 00:18:01 ]
@闲耘
第一个非空白和最后一个非空白的位置,这个用正则应该还是可以做到的,用预查应该可以做到,且不消耗存储。
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
<script type="text/javascript">//<![CDATA[
  var s = ' s ';
  /(?=[^\s])/.test(s);
  alert(RegExp.lastIndex);
//]]></script>

发表
表情图标
[smile] [confused] [cool] [cry]
[eek] [angry] [wink] [sweat]
[lol] [stun] [razz] [redface]
[rolleyes] [sad] [yes] [no]
[heart] [star] [music] [idea]
UBB代码
转换链接
表情图标
悄悄话
昵        称:  3-24字符, 不可使用特殊字符 *
安全规则: 请输入规则答案: 2+5=? *
 
Language Package
  • ENGLISH
  • 简体中文
用户面板
用户名:
密码:
安全规则: 2+5=?
注册
分类
  • Blog首页
  • Android [2] Android RSS Feed
  • Diary & Misc [115] Diary & Misc RSS Feed
  • Web Dev [108] Web Dev RSS Feed
  • Never Modules(JS) [12] Never Modules(JS) RSS Feed
  • Flash & Flex & Air [4] Flash & Flex & Air RSS Feed
  • PHP & Apache [1] PHP & Apache RSS Feed
  • XML [7] XML RSS Feed
  • CSS [7] CSS RSS Feed
  • ASP & .NET [3] ASP & .NET RSS Feed
  • Literature Archives [4] Literature Archives RSS Feed
  • Design [17] Design RSS Feed
  • Visual Basic [3] Visual Basic RSS Feed
最新评论
  • @gzman 那阵子确实想蛮多...
  • 兄弟,想太多了吧
  • [smile] [wink] [sweat] ...
  • javascript:insertSmilie...
  • 我新建了两个sliderbar都...
  • 不错哦````````
  • 好文,收藏至20ju.com
  • @aflyhorse 我这里没有实...
  • 我上次找了一个识别效果...
  • 第一个实例我只看见了doe...
  • 好文啊,只是我阅读太慢...
  • 其实用<iframe src="java...
  • 还没到那个境界。
  • @tokki 要造也应该造车,...
  • 能不造轮子的程序员太少...
搜索

统计数据
日志: 283
评论: 845
引用: 0
用户: 115
到访: 4059700
在线: 1

新浪微博
Links
  • Zerray
  • realdodo
  • ps album
  • my flickr
  • XiaoFeng
  • 神~ORZ
  • Jiuan's blog
  • yanpeng's blog
  • zhoux's blog
  • winter
  • aoao
  • jerry.qu
  • JoelLeung
  • monyer
  • Miller
  • PuterJam
  • Terry
  • JK
  • akira
  • dh20156's New World!
  • muxrwc
  • Joshua
  • Estyle
  • 互联网人
  • 兔子
  • 电脑爱好者
  • 阿笨狗
Favorite
  • leica china
  • Douglas Crockford
  • dhteumeuleu
  • regexplib
  • webfx
  • ajaxian
  • John Resig
  • dean
  • Adam McCrea
  • css beauty
  • livepipe
  • smashing magazine
  • ericlippert
  • narcissus
  • PPK
widget

Powered by LBS Version 2.0.304 © 2003-2005 SiC/CYAN. - Template writen by never-online - 桂ICP备07010684号
17 DB Queries | Proccessed in 125ms