javascript 更改 RegExp 标志

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5835469/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-25 18:31:45  来源:igfitidea点击:

Changing the RegExp flags

javascriptregexprototypeflags

提问by Witiko

So basically I wrote myself this function so as to be able to count the number of occurances of a Substring in a String:

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

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

The method itself performs rather well and both the RegExp and String based searches are quite comparable as to the execution time (both ~2ms on the entire vast Ray Bradbury's "451 Fahrenheit" searching for all the "the"s).

该方法本身执行得相当好,并且基于 RegExp 和字符串的搜索在执行时间方面都非常相似(在整个 Ray Bradbury 的“451 Fahrenheit”搜索所有“the”的整个过程中均约为 2 毫秒)。

What sort of bothers me, though, is the impossibility of changing the flag of the supplied RegExp instance. There is no point in calling String.prototype.matchin this function without the global flag of the supplied Regular Expression set to true, as it would only note the first occurance then. You could certainly set the flag manually on each RegExp passed to the function, I'd however prefer being able to clone and then manipulate the supplied Regular Expression's flags.

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

Astonishingly enough, I'm not permitted to do so as the RegExp.prototype.globalflag (more precisely all flags) appear to be read-only. Thence the commented-out line 8.

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

So my question is: Is there a niceway of changing the flags of a RegExp object?

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

I don't really wanna do stuff like this:

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

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

Some implementations might not event support the RegExp.prototype.toStringand simply inherit it from the Object.prototype, or it could be a different formatting entirely. And it just seems as a bad coding practice to begin with.

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

采纳答案by ridgerunner

First, your current code does not work correctly when needleis a regex which does not match. i.e. The following line:

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

return this.match(needle).length;

The matchmethod returns nullwhen there is no match. A JavaScript error is then generated when the lengthproperty of nullis (unsuccessfully) accessed. This is easily fixed like so:

当没有匹配时,该match方法返回null。当(不成功)访问的length属性时,null会生成 JavaScript 错误。这很容易修复,如下所示:

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

Now to the problem at hand. You are correct when you say that global, ignoreCaseand multilineare read only properties. The only option is to create a new RegExp. This is easily done since the regex source string is stored in the re.sourceproperty. Here is a tested modified version of your function which corrects the problem above and creates a new RegExp object when needledoes not already have its globalflag set:

现在到手头的问题。你说的是对的globalignoreCase并且multiline是只读属性。唯一的选择是创建一个新的 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;
};

回答by David Ruttka

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

Live DemoEDIT: The m was only for the sake of demonstrating that you can set multiple modifiers

现场演示编辑: m 只是为了证明您可以设置多个修饰符

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

回答by ChaosPandion

There isn't much you can do but I highly recommend you avoid using eval. You can extend the RegExp prototype to help you out.

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

回答by kevin cline

r = new Regexp(r.source, r.flags + (r.global ? "" : "g"));