JS编程建议——48:慎用正则表达式修剪字符串

建议48:慎用正则表达式修剪字符串
(1)使用两个子表达式修剪字符串
去除字符串首尾的空格是一个简单而常见的任务,但到目前为止JavaScript 还没有实现它。正则表达式允许用很少的代码实现一个修剪函数,最好的全面解决方案可能是使用两个子表达式:一个用于去除头部空格,另一个用于去除尾部空格。这样处理简单而快速,特别是处理长字符串时。
if(!String.prototype.trim) {

String.prototype.trim = function() {
    return this.replace(/^\s+/, "").replace(/\s+$/, "");
}

}
var str = " tn test string ".trim();
alert(str == "test string"); // alerts "true"
使用if语句进行检测,如果已经存在trim原生函数,则不要覆盖trim原生函数,因为原生函数进行了优化后通常远远快于自定义函数。使用上面代码在Firefox浏览器中大约有35%的性能提升(或多或少依赖于目标字符串的长度和内容)。将/s+$/(第二个正则表达式)替换成/ss*$/。虽然这两个正则表达式的功能完全相同,但是Firefox浏览器却为那些以非量词字元开头的正则表达式提供额外的优化。在其他浏览器上,差异不显著,或者优化完全不同。
然而,改变正则表达式,在字符串开头匹配/^ss*/不会产生明显差异,因为^锚需要“照顾”那些快速作废的非匹配位置(避免一个轻微的性能差异,因为在一个长字符串中可能产生上千次匹配尝试)。
(2)使用一个正则表达式修剪字符串
事实上,除这里列出的方法外还有许多其他方法,可以写一个正则表达式来修剪字符串,但在处理长字符串时,这种方法执行速度总比用两个简单的表达式要慢。
String.prototype.trim = function() {

return this.replace(/^\s+|\s+$/g, "");

}
这可能是最通常的解决方案。它通过分支功能合并了两个简单的正则表达式,并使用/g(全局)标记替换所有匹配,而不只是第一个匹配(当目标字符串首尾都有空格时将匹配两次)。这并不是一个“可怕”的方法,但在对长字符串操作时,它比使用两个简单的子表达式要慢,因为两个分支选项都要测试每个字符位置。
String.prototype.trim = function() {

return this.replace(/^\s*([\s\S]*?)\s*$/, "$1");

}
这个正则表达式的工作原理是匹配整个字符串,捕获从第一个到最后一个非空格字符之间的序列,记入后向引用1。然后使用后向引用1 替代整个字符串,就留下了这个字符串的修剪版本。
这个方法概念简单,但捕获组中的“懒惰”量词使正则表达式进行了许多额外操作(如回溯),因此在操作长目标字符串时很慢。在进入正则表达式捕获组时,[sS]类的“懒惰”量词?要求捕获组尽可能地减少重复次数。因此,这个正则表达式每匹配一个字符,都要停下来尝试匹配余下的s$模板。如果由于字符串当前位置之后存在非空格字符而导致匹配失败,正则表达式将匹配一个或多个字符,更新后向引用,然后再次尝试匹配模板的剩余部分。
String.prototype.trim = function() {

return this.replace(/^\s*([\s\S]*\S)?\s*$/, "$1");

}
这个表达式与上一个很像,但出于性能原因以“贪婪”量词取代了“懒惰”量词。为确保捕获组只匹配到最后一个非空格字符,必须尾随一个S。然而,由于正则表达式必须匹配全部由空格组成的字符串,整个捕获组通过尾随一个?量词而成为可选组。
在此,[sS]中的“贪婪”量词“”表示重复方括号中的任意字符模板直至字符串结束。然后,正则表达式每次回溯一个字符,直到它能够匹配后面的S,或者直到回溯到第一个字符而匹配整个组(之后它跳过这个组)。
如果尾部空格不比其他字符串更多,通过一个表达修剪的方案通常比前面那些使用“懒惰”量词的方案更快。事实上,这个方案在IE、Safari、Chrome和Opera浏览器上执行速度如此之快,甚至超过使用两个子表达式的方案,是因为这些浏览器包含特殊优化,专门服务于为字符类匹配任意字符的“贪婪”重复操作,正则表达式引擎直接跳到字符串末尾而不检查中间的字符(尽管回溯点必须被记下来),然后适当回溯。不幸的是,这种方法在Firefox 和Opera 9 浏览器上执行得非常慢,所以到目前为止,使用两个子表达式仍然是更好的跨浏览器方案。
String.prototype.trim = function() {

return this.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1");

}
这是一个相当普遍的方法,但没有很好的理由使用它,因为它在所有浏览器上都是这里所列出的所有方法中执行得最慢的一个。这类似于最后两个正则表达式,它匹配整个字符串然后用打算保留的部分替换这个字符串,因为内部组每次只匹配一个单词,正则表达式必须执行大量的离散步骤。修剪短字符串时性能冲击并不明显,但处理包含多个词的长字符串时,这个正则表达式可以成为影响性能的一个问题。
将内部组修改为一个非捕获组,例如,将(s+S+)修改为(?:s+S+),在Opera、IE和Chrome 浏览器上缩减了大约20%~45%的处理时间,在Safari 和Firefox 浏览器上也有轻微改善。尽管如此,一个非捕获组不能完全代换这个实现。注意,外部组不能转换为非捕获组,因为它在被替换的字符串中被引用了。
虽然正则表达式的执行速度很快,但是没有它们帮助时修剪字符串的性能还是值得考虑的。例如:
String.prototype.trim = function() {

var start = 0,
end = this.length - 1,
ws = " \n\r\t\f\x0b\xa0\u1680\u180e\u2000\u2001\u2002\u2003
\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u202f
\u205f\u3000\ufeff";
while (ws.indexOf(this.charAt(start)) > -1) {
    start++;
}
while (end > start && ws.indexOf(this.charAt(end)) > -1) {
    end--;
}
return this.slice(start, end + 1);

}
在上面代码中,ws变量包括在ECMAScript v5中定义的所有空白字符。出于效率方面的考虑,在得到修剪区的起始和终止位置之前避免复制字符串的任何部分。
当字符串末尾只有少量空格时,这种情况使正则表达式处于无序状态。原因是,尽管正则表达式很好地去除了字符串头部的空格,却不能同样快速地修剪长字符串的尾部。一个正则表达式不能跳到字符串的末尾而不考虑沿途字符。正因如此,在第二个while循环中从字符串末尾向前查找一个非空格字符。
虽然上面代码不受字符串总长度影响,但是它有自己的弱点—长的头尾空格,因为循环检查字符是不是空格在效率上不如正则表达式所使用的优化过的搜索代码。
(3)正则表达式与非正则表达式结合起来修剪字符串
最后一个办法是将正则表达式与非正则表达式两者结合起来,用正则表达式修剪头部空格,用非正则表达式方法修剪尾部字符。
String.prototype.trim = function() {

var str = this.replace(/^\s+/, ""),
end = str.length - 1,
ws = /\s/;
while (ws.test(str.charAt(end))) {
    end--;
}
return str.slice(0, end + 1);

}
当只修剪一个空格时,此混合方法非常快,同时去除了性能上的风险,如以长空格开头的字符串,完全由空格组成的字符串(尽管它在处理尾部长空格的字符串时仍具有弱点)。
注意:此方案在循环中使用正则表达式检测字符串尾部的字符是否为空格,虽然使用正则表达式增加了一点性能负担,但是它允许根据浏览器定义空格字符列表,以保持简短和兼容性。
所有修剪方法总的趋势:在基于正则表达式的方案中,字符串总长比修剪掉的字符数量更影响性能;而非正则表达式方案从字符串末尾反向查找,不受字符串总长的影响,但明显受到修剪空格数量的影响。简单地使用两个子正则表达式在所有浏览器上处理不同内容和长度的字符串时,均表现出稳定的性能,因此可以说这种方案是最全面的解决方案。混合解决方案在处理长字符串时特别快,其代价是代码稍长,在某些浏览器上处理尾部长空格时存在弱点。

时间: 2024-09-13 23:46:13

JS编程建议——48:慎用正则表达式修剪字符串的相关文章

JS使用replace()方法和正则表达式进行字符串的搜索与替换实例

 这篇文章主要介绍了JS使用replace()方法和正则表达式进行字符串的搜索与替换实例,需要的朋友可以参考下 1.JS字符串的替换及replace()方法的使用   replace(regexp,replacement)方法有两个参数,第一参数可以是一个纯文本字符串或是一个RegExp对象,具体请看RegExp对象的使用:第二个参数可是一个字符串也可以是一个函数.   以下是JS字符串替换的举例:   例1:   代码如下: var str="Hello world!"; docum

JS编程建议——35:获取字节长度

建议35:获取字节长度String对象的length属性能够返回字符串的长度,不管字符是单字节,还是双字节,都作为一个来计算.因此,要获取字符串的字节长度,必须通过手工计算获取,这里介绍两种方法.1)第一种方法是利用循环结构枚举每个字符,并根据字符的字符编码,判断当前字符是单字节还是双字节,然后递加字符串的字节数. String.prototype.lengthB = function( ){ var b = 0, l = this.length; if( l ){ for( var i = 0

JS编程建议——38:正确认识正则表达式工作机制

建议38:正确认识正则表达式工作机制有很多因素影响正则表达式的效率.首先,正则表达式适配的文本千差万别,部分匹配时比完全不匹配所用的时间要长.其次,每种浏览器的正则表达式引擎也有不同的内部优化.要有效使用正则表达式,重要的是理解它们的工作机制.一个正则表达式处理的基本步骤如下:第1步,编译.在创建了一个正则表达式对象后,浏览器先要检查模板有没有错误,然后将它转换成一个本机代码例程,用于执行匹配工作.如果将正则表达式赋给一个变量,就可以避免重复执行此步骤.第2步,设置起始位置.当一个正则表达式投入

JS编程建议——41:正确使用正则表达式引用

建议41:正确使用正则表达式引用正则表达式在执行匹配运算时会自动把每个分组(子表达式)匹配的文本都存储在一个特殊的地方以备将来使用.这些存储在分组中的特殊值被称为反向引用.反向引用将遵循从左到右的顺序,根据表达式中左括号字符的顺序进行创建和编号.var s = "abcdefghijklmn";var r = /(a(b(c)))/;var a = s.match(r); //["abc", "abc" , "bc" , &q

JS编程建议——42:用好正则表达式静态值

建议42:用好正则表达式静态值正则表达式的静态属性比较特殊,有两个名字:长名(全称)和短名(简称,以美元符号开头表示),详细说明见表2.1.表2.1 RegExp的静态属性长名 短名 说明input $_ 最后用于匹配的字符串,即传递给exec()或test()方法的字符串lastMatch $& 最后匹配的字符lastParen $+ 最后匹配的分组leftContext $` 在上次匹配之前的子字符串multiline $* 用于指定是否所有表达式都使用多行模式的布尔值rightContex

JS编程建议——47:避免使用正则表达式的场景

建议47:避免使用正则表达式的场景正则表达式匹配速度是非常快的.然而,当只搜索文字字符串时正则匹配经常会显得多余,尤其当事先知道了字符串的哪一部分将要被测试时.例如,要检查一个字符串是不是以分号结束,可以使用:endsWithSemicolon = /;$/.test(str);当前没有哪个浏览器"聪明"到这个程度,能够意识到这个正则表达式只能匹配字符串的末尾.最终它们所做的将是一个一个地测试整个字符串.每当发现了一个分号,正则表达式就前进到下一个字元($),检查它是否匹配字符串的末尾

JS编程建议——46:提高正则表达式执行效率

建议46:提高正则表达式执行效率(1)关注如何让匹配更快失败正则表达式处理慢往往是因为匹配失败过程慢,而不是匹配成功过程慢.使用正则表达式匹配一个很大字符串的一小部分,情况更为严重,正则表达式匹配失败的位置比匹配成功的位置要多得多.一个修改使正则表达式匹配更快但失败更慢,例如,通过增加所需的回溯次数尝试所有分支的排列组合,这通常是一个失败的修改.(2)正则表达式以简单的.必需的字元开始最理想的情况是,一个正则表达式的起始字元应当尽可能快速地测试并排除明显不匹配的位置.用于此目的好的起始字元通常是

JS编程建议——36:警惕字符串连接操作(1)

建议36:警惕字符串连接操作(1)字符串连接表现出惊人的"性能紧张".一个任务通过一个循环向字符串末尾不断地添加内容,以创建一个字符串.例如,创建一个HTML 表或一个XML 文档.此类处理在一些浏览器上表现得非常糟糕.当连接少量字符串时,这些问题都可以忽略,临时使用可选择最熟悉的操作.当合并字符串的长度和数量增加之后,有些函数开始显示出"威力".(1)+.+=+.+=运算符提供了连接字符串的最简单方法.除IE 7及其以前版本外,当前所有浏览器都对这种方法优化得很好

JS编程建议——36:警惕字符串连接操作(2)

建议36:警惕字符串连接操作(2)先将两个小字符串合并起来,然后将结果返回给大字符串.创建中间字符串s1 + s2与两次复制大字符串相比,对性能的"冲击"要轻得多.(2)编译期合并在赋值表达式中所有字符串连接都属于编译期常量,Firefox自动地在编译过程中合并它们.在以下这个方法中可看到这一过程: function foldingDemo() { var str = "compile" + "time" + "folding"