在JavaScript中检查字符串是否为空白(即只包含空格)的最高效方法
The most performant way to check if a string is blank (i.e. only contains whitespace) in JavaScript?
我需要写一个函数来测试,如果给定的字符串在某种意义上是"空白",它只包含空白字符。空格字符如下:
''u0009',
''u000A',
''u000B',
''u000C',
''u000D',
' ',
''u0085',
''u00A0',
''u1680',
''u180E',
''u2000',
''u2001',
''u2002',
''u2003',
''u2004',
''u2005',
''u2006',
''u2007',
''u2008',
''u2009',
''u200A',
''u2028',
''u2029',
''u202F',
''u205F',
''u3000'
函数将被调用很多次,所以它必须非常非常高效。但不应该占用太多内存(比如将数组中的每个字符映射为true/false)。到目前为止我尝试过的事情:
- regexp -性能不太好
- 修剪并检查长度是否为0 -性能不太好,还使用额外的内存来保存修剪过的字符串
- 根据包含空白字符(
if (!whitespaceCharactersMap[str[index]]) ...
)的散列集检查每个字符串字符-工作得足够好 我当前的解决方案使用硬编码比较:
function(str) { var length = str.length; if (!length) { return true; } for (var index = 0; index < length; index++) { var c = str[index]; if (c === ' ') { // skip } else if (c > ''u000D' && c < ''u0085') { return false; } else if (c < ''u00A0') { if (c < ''u0009') { return false; } else if (c > ''u0085') { return false; } } else if (c > ''u00A0') { if (c < ''u2028') { if (c < ''u180E') { if (c < ''u1680') { return false; } else if(c > ''u1680') { return false; } } else if (c > ''u180E') { if (c < ''u2000') { return false; } else if (c > ''u200A') { return false; } } } else if (c > ''u2029') { if (c < ''u205F') { if (c < ''u202F') { return false; } else if (c > ''u202F') { return false; } } else if (c > ''u205F') { if (c < ''u3000') { return false; } else if (c > ''u3000') { return false; } } } } } return true; }
这似乎比哈希集(在Chrome上测试)快50-100%。
有人看到或知道进一步的选择吗?
更新1
我将在这里回答一些评论:
- 它不只是检查用户输入是否为空。我必须解析某些数据格式,其中空格必须单独处理。
- 值得优化。我以前对代码进行过分析。检查空白字符串似乎是一个问题。而且,正如我们所看到的,不同方法之间的性能差异可能高达10倍,这绝对值得努力。
- 一般来说,我发现这种"哈希集vs.正则表达式vs.开关vs.分支"的挑战非常有教育意义。
- 我需要浏览器以及node.js的相同功能。
现在这是我对性能测试的看法:
http://jsperf.com/hash-with-comparisons/6如果你们能多做几次这些测试我会很感激的。
初步结论:- branchlessTest (
a^9*a^10*a^11...
)在Chrome和Firefox中非常快,但在Safari中不是。从性能的角度来看,这可能是Node.js的最佳选择。 - switchTest在chrome和Firefox上也相当快,但是,令人惊讶的是在Safari和Opera中最慢
- regexp with re.test(str)在任何地方都表现良好,在Opera中甚至最快。
- 哈希和分支几乎在任何地方都显示出几乎相同的糟糕结果。比较也类似,往往表现最差(这可能是由于实现,检查
' '
应该是第一个)。
总而言之,对于我的情况,我将选择以下regexp版本:
var re = /[^'s]/;
return !re.test(str);
原因:
- 无分支版本在Chrome和Firefox中很酷,但不太便携
- 在Safari中切换太慢
- regexp似乎在任何地方都表现良好,它们在代码中也非常紧凑
硬编码的解决方案似乎是最好的,但我认为switch
应该更快。这取决于JavaScript解释器处理这些的方式(大多数编译器都非常有效地做到这一点),所以它可能是特定于浏览器的(即,在某些地方快,在其他地方慢)。另外,我不确定JavaScript处理utf字符串有多快,所以您可以尝试在比较值之前将字符转换为整数代码。
for (var index = 0; index < length; index++)
{
var c = str.charCodeAt(index);
switch (c) {
case 0x0009: case 0x000A: case 0x000B: case 0x000C: case 0x000D: case 0x0020:
case 0x0085: case 0x00A0: case 0x1680: case 0x180E: case 0x2000: case 0x2001:
case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007:
case 0x2008: case 0x2009: case 0x200A: case 0x2028: case 0x2029: case 0x202F:
case 0x205F: case 0x3000: continue;
}
return false;
}
另一件要考虑的事情是改变for
:
for (var index in str)
{
...
}
编辑
您的jsPerf测试得到了一些修订,当前的版本在这里。我的代码在Chrome 26和27以及IE10中要快得多,但在Firefox 18中也是最慢的。
我在64位Linux上的Firefox 20.0上运行了相同的测试(我不知道如何使jsPerf保存这些),结果证明它是两个最快的之一(与trimTest
并列,都是大约11.8M ops/sec)。我还在WinXP上测试了Firefox 20.0.1,但在VirtualBox(仍然在64位Linux下,这里可能会有很大的不同)下,switchTest
的速度为10M ops/sec, trimTest
的速度为7.3M ops/sec。
所以,我猜性能取决于浏览器版本和/或甚至可能取决于底层操作系统/硬件(我想上面的FF18测试是在Win上进行的)。无论如何,要制作真正的最佳版本,你必须制作许多版本,在所有浏览器、操作系统、架构上测试每个版本……你可以获取,然后在你的页面中包含最适合访问者浏览器、操作系统、架构的版本……但是,我不确定哪种代码值得这么麻烦。
由于分支比大多数其他操作要昂贵得多,因此您希望将分支保持在最低限度。因此,if/else语句的序列可能不是很高效。一种主要使用数学的方法会快得多。例如:
执行相等性检查而不使用任何分支的一种方法是使用位操作。一个例子是,检查a == b:
a ^ b == 0
由于两个相似位(即1 ^ 1或0 ^ 0)的xor为0,因此两个相等的值的xor为0。这很有用,因为它允许我们将0视为"真"值,并进行更多的数学运算。假设我们有一堆布尔变量以这种方式表示:非零的数字为假,零表示真。如果我们想问,"这些都是真的吗?"我们只需要把它们相乘。如果其中任何一个为真(等于0),则整个结果将为0。
因此,例如,代码看起来像这样:function(str) {
for (var i = 0; i < str.length; i++) {
var c = str[i];
if ((c ^ ''u0009') * (c ^ ''u000A') * (c ^ ''u000B') ... == 0)
continue;
return false;
}
return true;
}
这样做比简单地做一些事情更高效的主要原因:
if ((c == ''u0009') || (c == ''u000A') || (c == ''u0008') ...)
是JavaScript有短路布尔运算符,这意味着每次使用||
运算符时,它不仅执行或操作,而且还检查它是否可以证明该语句到目前为止必须为真,这是一个分支操作,这是昂贵的。另一方面,数学方法不涉及分支,除了if语句本身,因此应该要快得多。
这将对字符串的字符创建并使用'散列'查找,如果检测到非空白则返回false:
var wsList=[''u0009',''u000A',''u000B',''u000C',''u000D',' ',''u0085',''u00A0',''u1680',''u180E',''u2000',''u2001',''u2002',''u2003',''u2004',''u2005',''u2006',''u2007',''u2008',''u2009',''u200A',''u2028',''u2029',''u202F',''u205F',''u3000'];
var ws=Object.create(null);
wsList.forEach(function(char){ws[char]=true});
function isWhitespace(txt){
for(var i=0, l=txt.length; i<l; ++i){
if(!ws[txt[i]])return false;
}
return true;
}
var test1=" 'u1680 'u000B 'u2002 'u2004";
isWhitespace(test1);
/*
true
*/
var test2=" _ . a ";
isWhitespace(test2);
/*
false
*/
不确定它的性能(yet)。在jsperf上进行快速测试后,与使用/^'s*$/
的RegExp相比,它非常慢。
编辑:
看来,您应该使用的解决方案可能取决于您正在处理的数据的性质:数据主要是空白还是主要是非空白?也主要是ascii范围的文本?对于一般的测试用例,您可以通过对常见的非空白字符范围使用范围检查(通过if
),对最常见的空白使用switch
,然后对其他所有内容使用散列查找来加快速度。如果被测试的大多数数据由最常见的字符(在0x0—0x7F之间)组成,这可能会提高测试的平均性能。
也许像这样的东西(if/switch/hash的混合)可以工作:
/*same setup as above with variable ws being a hash lookup*/
function isWhitespaceHybrid(txt){
for(var i=0, l=txt.length; i<l; ++i){
var cc=txt.charCodeAt(i)
//above space, below DEL
if(cc>0x20 && cc<0x7F)return false;
//switch only the most common whitespace
switch(cc){
case 0x20:
case 0x9:
case 0xA:
case 0xD:
continue;
}
//everything else use a somewhat slow hash lookup (execute for non-ascii range text)
if(!ws[txt[i]])return false;
}
return true;
}
- 访问键中包含空格的嵌套属性
- Meteor:如何使用空格键创建包含两个集合数据的树列表
- 通过JavaScript或jQuery删除P标记,其中包含不间断的空格
- 如何在Javascript中检查字符串是否包含多个空格并返回
- 正则表达式 - 字符串不能以空格开头,以空格结尾,并且连续包含几个空格
- Javascript 如果字符串包含空格,则用破折号替换空格
- 在自定义标记中查找包含特定值的元素,并带有空格分隔的值
- 当数据文本字段预操作包含空格时出现 KendoTreeView 错误
- 如何使用 javascript 确定字符串是否只包含空格
- 用于匹配文本的正则表达式仅包含空格和 br 标记
- 使用 JQuery 和 append() 将包含空格的文本数组添加到 TextArea 中
- 如何检查字符串是否不包含任何内容或仅包含空格
- 使用包含括号、分号和空格的正则表达式拆分字符串时出现问题
- CSS:选择名称中包含空格的类
- jQuery(document).width() 不包含 “空格”
- 正则表达式检查字符串是否仅包含字母数字字符和空格 - javascript
- HTML5 输入类型=数字值在 Webkit 中为空,如果包含空格或非数字字符
- 如何使用jQuery隐藏仅包含空格的HTML元素
- ng如果没有'如果条件文本在AngularJs中包含空格,则不起作用
- 如果在调用函数时提供的参数包含空格,则不调用函数