是否有允许正则表达式的JavaScript的String.indexOf()版本?

Pat*_*Pat 201 javascript regex indexof

在javascript中,是否有一个等效的String.indexOf()为第一个第一个参数采用正则表达式而不是字符串,同时仍然允许第二个参数?

我需要做点什么

str.indexOf(/[abc]/ , i);
Run Code Online (Sandbox Code Playgroud)

str.lastIndexOf(/[abc]/ , i);
Run Code Online (Sandbox Code Playgroud)

虽然String.search()将regexp作为参数,但它不允许我指定第二个参数!

编辑:
事实证明这比我原先想象的要难,所以我编写了一个小测试函数来测试所有提供的解决方案......它假设regexIndexOf和regexLastIndexOf已被添加到String对象中.

function test (str) {
    var i = str.length +2;
    while (i--) {
        if (str.indexOf('a',i) != str.regexIndexOf(/a/,i)) 
            alert (['failed regexIndexOf ' , str,i , str.indexOf('a',i) , str.regexIndexOf(/a/,i)]) ;
        if (str.lastIndexOf('a',i) != str.regexLastIndexOf(/a/,i) ) 
            alert (['failed regexLastIndexOf ' , str,i,str.lastIndexOf('a',i) , str.regexLastIndexOf(/a/,i)]) ;
    }
}
Run Code Online (Sandbox Code Playgroud)

我正在测试如下以确保至少对于一个字符regexp,结果与我们使用indexOf时相同

//在xes
test('xxx')中查找a ;
试验( 'AXX');
试验( 'XAX');
试验( 'XXA');
试验( 'AXA');
试验( '的Xaa');
试验( 'AAX');
试验( 'AAA');

Gle*_*enn 173

所述的实例String构造有一个.search()方法,它接受一个正则表达式并返回第一匹配的索引.

要从特定位置开始搜索(伪造第二个参数.indexOf()),您可以slice关闭第一个i字符:

str.slice(i).search(/re/)
Run Code Online (Sandbox Code Playgroud)

但是这将得到较短的字符串中的索引(在第一部分被切掉之后),所以你想要将截断的part(i)的长度添加到返回的索引(如果不是)-1.这将为您提供原始字符串中的索引:

function regexIndexOf(text, re, i) {
    var indexInSuffix = text.slice(i).search(re);
    return indexInSuffix < 0 ? indexInSuffix : indexInSuffix + i;
}
Run Code Online (Sandbox Code Playgroud)

  • str.substr(ⅰ).search(/重新/) (14认同)
  • 很好的解决方案,但输出有点不同.indexOf将从头开始返回一个数字(无论偏移量如何),而这将从偏移量返回位置.所以,对于奇偶校验,你会想要更像这样的东西:`function regexIndexOf(text,offset){var initial = text.substr(offset).search(/ re /); if(initial> = 0){initial + = offset; } return initial; }` (5认同)
  • 来自问题:虽然 String.search() 采用正则表达式作为参数,但它不允许我指定第二个参数! (2认同)

Jas*_*ing 124

结合已经提到的一些方法(indexOf显然相当简单),我认为这些功能可以解决这个问题:

String.prototype.regexIndexOf = function(regex, startpos) {
    var indexOf = this.substring(startpos || 0).search(regex);
    return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf;
}

String.prototype.regexLastIndexOf = function(regex, startpos) {
    regex = (regex.global) ? regex : new RegExp(regex.source, "g" + (regex.ignoreCase ? "i" : "") + (regex.multiLine ? "m" : ""));
    if(typeof (startpos) == "undefined") {
        startpos = this.length;
    } else if(startpos < 0) {
        startpos = 0;
    }
    var stringToWorkWith = this.substring(0, startpos + 1);
    var lastIndexOf = -1;
    var nextStop = 0;
    while((result = regex.exec(stringToWorkWith)) != null) {
        lastIndexOf = result.index;
        regex.lastIndex = ++nextStop;
    }
    return lastIndexOf;
}
Run Code Online (Sandbox Code Playgroud)

显然,修改内置String对象会为大多数人发送红色标记,但这可能是一次没有那么大的交易; 只是意识到这一点.


更新:编辑regexLastIndexOf(),这似乎lastIndexOf()现在模仿.如果仍然失败并在什么情况下,请告诉我.


更新:通过本页评论中找到的所有测试,以及我自己的测试.当然,这并不意味着它是防弹的.任何反馈意见.

  • 我认为使用`regex.lastIndex = result.index + 1;`代替`regex.lastIndex = ++ nextStop;`更有效率.它会更快地进入下一场比赛而不会失去任何结果. (2认同)
  • 如果您更喜欢从 npm 中提取它,这两个 util 函数现在在 NPM 上为:https://www.npmjs.com/package/index-of-regex (2认同)

pmr*_*ule 36

我有一个简短的版本.这对我来说很有效!

var match      = str.match(/[abc]/gi);
var firstIndex = str.indexOf(match[0]);
var lastIndex  = str.lastIndexOf(match[match.length-1]);
Run Code Online (Sandbox Code Playgroud)

如果你想要一个原型版本:

String.prototype.indexOfRegex = function(regex){
  var match = this.match(regex);
  return match ? this.indexOf(match[0]) : -1;
}

String.prototype.lastIndexOfRegex = function(regex){
  var match = this.match(regex);
  return match ? this.lastIndexOf(match[match.length-1]) : -1;
}
Run Code Online (Sandbox Code Playgroud)

编辑:如果你想添加对fromIndex的支持

String.prototype.indexOfRegex = function(regex, fromIndex){
  var str = fromIndex ? this.substring(fromIndex) : this;
  var match = str.match(regex);
  return match ? str.indexOf(match[0]) + fromIndex : -1;
}

String.prototype.lastIndexOfRegex = function(regex, fromIndex){
  var str = fromIndex ? this.substring(0, fromIndex) : this;
  var match = str.match(regex);
  return match ? str.lastIndexOf(match[match.length-1]) : -1;
}
Run Code Online (Sandbox Code Playgroud)

要使用它,就像这样简单:

var firstIndex = str.indexOfRegex(/[abc]/gi);
var lastIndex  = str.lastIndexOfRegex(/[abc]/gi);
Run Code Online (Sandbox Code Playgroud)

  • 您的算法将在以下情况下崩溃: `"aRomeo Romeo".indexOfRegex(new RegExp("\\bromeo", 'gi'));` 结果应该是 7 时却是 1,因为 indexOf 会寻找“romeo”第一次出现,无论它是否位于单词的开头。 (2认同)

rmg*_*n3t 13

使用:

str.search(regex)
Run Code Online (Sandbox Code Playgroud)

请参阅此处的文档.

  • @OZZIE:不,不是真的.它基本上是[格伦的回答](/sf/answers/19166731/)(约150支票),除了**没有任何解释**,**不支持**起始位置除了`0`,并被发布了......**七年**之后. (9认同)

Mar*_*rot 7

根据BaileyP的回答.主要区别在于,-1如果模式无法匹配,则返回这些方法.

编辑:感谢Jason Bunting的回答,我有了一个主意.为什么不修改.lastIndex正则表达式的属性?虽然这只适用于带有全局标志(/g)的模式.

编辑:已更新以传递测试用例.

String.prototype.regexIndexOf = function(re, startPos) {
    startPos = startPos || 0;

    if (!re.global) {
        var flags = "g" + (re.multiline?"m":"") + (re.ignoreCase?"i":"");
        re = new RegExp(re.source, flags);
    }

    re.lastIndex = startPos;
    var match = re.exec(this);

    if (match) return match.index;
    else return -1;
}

String.prototype.regexLastIndexOf = function(re, startPos) {
    startPos = startPos === undefined ? this.length : startPos;

    if (!re.global) {
        var flags = "g" + (re.multiline?"m":"") + (re.ignoreCase?"i":"");
        re = new RegExp(re.source, flags);
    }

    var lastSuccess = -1;
    for (var pos = 0; pos <= startPos; pos++) {
        re.lastIndex = pos;

        var match = re.exec(this);
        if (!match) break;

        pos = match.index;
        if (pos <= startPos) lastSuccess = pos;
    }

    return lastSuccess;
}
Run Code Online (Sandbox Code Playgroud)


And*_*isi 6

你可以使用substr.

str.substr(i).match(/[abc]/);
Run Code Online (Sandbox Code Playgroud)


Pre*_*aul 5

RexExp实例已经具有lastIndex属性(如果它们是全局的),所以我正在做的是复制正则表达式,对其进行略微修改以满足我们的目的,将exec其放在字符串上并查看lastIndex。这将不可避免地比在字符串上循环快。(您有足够的示例说明如何将其放入字符串原型,对吗?)

function reIndexOf(reIn, str, startIndex) {
    var re = new RegExp(reIn.source, 'g' + (reIn.ignoreCase ? 'i' : '') + (reIn.multiLine ? 'm' : ''));
    re.lastIndex = startIndex || 0;
    var res = re.exec(str);
    if(!res) return -1;
    return re.lastIndex - res[0].length;
};

function reLastIndexOf(reIn, str, startIndex) {
    var src = /\$$/.test(reIn.source) && !/\\\$$/.test(reIn.source) ? reIn.source : reIn.source + '(?![\\S\\s]*' + reIn.source + ')';
    var re = new RegExp(src, 'g' + (reIn.ignoreCase ? 'i' : '') + (reIn.multiLine ? 'm' : ''));
    re.lastIndex = startIndex || 0;
    var res = re.exec(str);
    if(!res) return -1;
    return re.lastIndex - res[0].length;
};

reIndexOf(/[abc]/, "tommy can eat");  // Returns 6
reIndexOf(/[abc]/, "tommy can eat", 8);  // Returns 11
reLastIndexOf(/[abc]/, "tommy can eat"); // Returns 11
Run Code Online (Sandbox Code Playgroud)

您也可以将函数原型制作到RegExp对象上:

RegExp.prototype.indexOf = function(str, startIndex) {
    var re = new RegExp(this.source, 'g' + (this.ignoreCase ? 'i' : '') + (this.multiLine ? 'm' : ''));
    re.lastIndex = startIndex || 0;
    var res = re.exec(str);
    if(!res) return -1;
    return re.lastIndex - res[0].length;
};

RegExp.prototype.lastIndexOf = function(str, startIndex) {
    var src = /\$$/.test(this.source) && !/\\\$$/.test(this.source) ? this.source : this.source + '(?![\\S\\s]*' + this.source + ')';
    var re = new RegExp(src, 'g' + (this.ignoreCase ? 'i' : '') + (this.multiLine ? 'm' : ''));
    re.lastIndex = startIndex || 0;
    var res = re.exec(str);
    if(!res) return -1;
    return re.lastIndex - res[0].length;
};


/[abc]/.indexOf("tommy can eat");  // Returns 6
/[abc]/.indexOf("tommy can eat", 8);  // Returns 11
/[abc]/.lastIndexOf("tommy can eat"); // Returns 11
Run Code Online (Sandbox Code Playgroud)

关于如何修改的快速说明RegExp:因为indexOf我只需要确保设置了全局标志即可。对于lastIndexOf,我正在使用否定的超前查找来查找最后一次出现,除非RegExp该字符串已在字符串末尾匹配。