2011-04-29 76 views
13

所以基本上我写我自己这个功能,从而能算子串出现的次数在一个字符串:改变正则表达式标志

String.prototype.numberOf = function(needle) { 
    var num = 0, 
     lastIndex = 0; 
    if(typeof needle === "string" || needle instanceof String) { 
    while((lastIndex = this.indexOf(needle, lastIndex) + 1) > 0) 
     {num++;} return num; 
    } else if(needle instanceof RegExp) { 
    // needle.global = true; 
    return this.match(needle).length; 
    } return 0; 
}; 

方法本身执行得相当好两者的正则表达式和基于字符串的搜索与执行时间非常相似(在整个巨大的雷布拉德伯里的“451华氏度”搜索所有“这些”都是〜2ms。

但是,困扰我的是,不可能改变提供的RegExp实例的标志。在此函数中调用String.prototype.match是没有意义的,因为它只会记录第一次出现的情况,而不会将提供的正则表达式的全局标志设置为true。你当然可以在每个传递给函数的RegExp上手动设置标志,但是我希望能够克隆并操作提供的正则表达式标志。

令人惊讶的是,我不允许这样做,因为RegExp.prototype.global标志(更确切地说所有标志)看起来都是只读的。从那里注释掉的线8.

所以我的问题是:有没有不错改变RegExp对象的标志的方式?

我真的不想要做这样的东西:

if(!expression.global) 
    expression = eval(expression.toString() + "g"); 

某些实现可能不支持事件的RegExp.prototype.toString和简单地从Object.prototype中继承它,或它可能完全是一种不同的格式。这看起来是一种不好的编码习惯。

+0

我明白了。好吧,我编辑了这篇文章,所以你可以删除这个下降。 :-) – Witiko 2011-04-29 19:05:08

+0

完成并完成。对不起'回合! – ridgerunner 2011-04-29 19:16:21

回答

12

首先,当needle是不匹配的正则表达式时,您当前的代码无法正常工作。即下面的行:

return this.match(needle).length; 

match该方法返回null当不存在匹配。当nulllength属性(未成功)被访问时,会生成JavaScript错误。这是很容易固定,像这样:

var m = this.match(needle); 
return m ? m.length : 0; 

我们手头的问题。当你说global,ignoreCasemultiline是只读属性时,你是正确的。唯一的选择是创建一个新的RegExp。这很容易完成,因为正则表达式源字符串存储在re.source属性中。这里是你的函数其纠正上述问题,并创建一个新的RegExp对象时needle已经没有了global标志设置一个测试修改后的版本:

String.prototype.numberOf = function(needle) { 
    var num = 0, 
    lastIndex = 0; 
    if (typeof needle === "string" || needle instanceof String) { 
     while((lastIndex = this.indexOf(needle, lastIndex) + 1) > 0) 
      {num++;} return num; 
    } else if(needle instanceof RegExp) { 
     if (!needle.global) { 
      // If global flag not set, create new one. 
      var flags = "g"; 
      if (needle.ignoreCase) flags += "i"; 
      if (needle.multiline) flags += "m"; 
      needle = RegExp(needle.source, flags); 
     } 
     var m = this.match(needle); 
     return m ? m.length : 0; 
    } 
    return 0; 
}; 
+0

感谢您指出不一致。我喜欢这个解决方案,不用扩展带有标志函数的RegExp原型(如上所述)可能更安全。 – Witiko 2011-04-29 18:55:05

+0

更好的是,使用'myRegex.test(str)'如果这就是你所关心的。它既短又快。 – Phrogz 2011-04-29 20:18:19

+0

并不完全,我试图计算所有的发生。 :-) – Witiko 2011-04-30 09:57:25

8
var globalRegex = new RegExp(needle.source, "g"); 

Live Demo编辑:m个是只为证明自己可设置多个调节

var regex = /find/; 
var other = new RegExp(regex.source, "gm"); 
alert(other.global); 
alert(other.multiline); 
4

没有什么可以做,但我强烈建议你避免着想使用eval。您可以扩展RegExp原型以帮助您。

RegExp.prototype.flags = function() { 
    return (this.ignoreCase ? "i" : "") 
     + (this.multiline ? "m" : "") 
     + (this.global ? "g" : ""); 
}; 

var reg1 = /AAA/i; 
var reg2 = new RegExp(reg1.source, reg1.flags() + 'g'); 
+0

我刚才想出了.source的一部分。但是,我完全忘记了旗帜不属于消息来源的事实。 :)我想我会融入这个想法。谢谢;) – Witiko 2011-04-29 18:21:53