【问题标题】:Changing the RegExp flags更改 RegExp 标志
【发布时间】:2011-08-15 16:21:40
【问题描述】:

所以基本上我自己编写了这个函数,以便能够计算字符串中子字符串的出现次数:

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;
};

该方法本身执行得相当好,并且基于 RegExp 和字符串的搜索在执行时间方面都相当可比(在整个庞大的 Ray Bradbury 的“451 Fahrenheit”中搜索所有“the”时都约为 2ms)。

然而,让我感到困扰的是无法更改提供的 RegExp 实例的标志。如果不将提供的正则表达式的全局标志设置为 true,则在此函数中调用 String.prototype.match 是没有意义的,因为它只会记录第一次出现。您当然可以在传递给函数的每个 RegExp 上手动设置标志,但是我更喜欢能够克隆然后操作提供的正则表达式的标志。

令人惊讶的是,我不允许这样做,因为 RegExp.prototype.global 标志(更准确地说是所有标志)似乎是只读的。然后是注释掉的第 8 行。

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

我真的不想做这样的事情:

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

某些实现可能不支持 RegExp.prototype.toString 并且只是从 Object.prototype 继承它,或者它可能是完全不同的格式。这似乎是一种糟糕的编码习惯。

【问题讨论】:

  • 我明白了。好吧,我编辑了帖子,所以你可以删除降价。 :-)
  • 完成了。对不起!

标签: javascript regex prototype flags


【解决方案1】:
var globalRegex = new RegExp(needle.source, "g");

Live Demo EDIT:m只是为了展示你可以设置多个修饰符

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

【讨论】:

    【解决方案2】:

    您无能为力,但我强烈建议您避免使用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');
    

    【讨论】:

    • 我刚刚发现了 .source 部分。但是,我完全忘记了标志不是源的一部分这一事实。 :) 我想我会采纳这个想法。谢谢;)
    【解决方案3】:

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

    return this.match(needle).length;
    

    match 方法在没有匹配项时返回 null。当(未成功)访问nulllength 属性时,会生成一个JavaScript 错误。这很容易修复,如下所示:

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

    现在解决手头的问题。当您说globalignoreCasemultiline 是只读属性时,您是对的。唯一的选择是创建一个新的 RegExp。这很容易做到,因为正则表达式源字符串存储在re.source 属性中。这是您的函数的经过测试的修改版本,它纠正了上述问题并在 needle 尚未设置其 global 标志时创建一个新的 RegExp 对象:

    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;
    };
    

    【讨论】:

    • 感谢您指出不一致之处。我喜欢这个解决方案,不使用标志函数扩展 RegExp 原型可能更安全(如上所述)。
    • 更好的是,如果您只关心这些,请使用myRegex.test(str)。它既短又快。
    • 不完全是,我正在尝试计算所有出现的次数。 :-)
    【解决方案4】:
    r = new Regexp(r.source, r.flags + (r.global ? "" : "g"));
    

    【讨论】:

    • 请注意,r.global ? "" : "g" 周围的 () 是必要的,因为条件运算符的优先级低于加法。这让我绊倒了,所以要小心。
    猜你喜欢
    • 2012-07-26
    • 2013-09-28
    • 2022-11-10
    • 2014-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-06
    相关资源
    最近更新 更多