在javascript中,是否有一个等效的String.indexOf()为第一个第一个参数采用正则表达式而不是字符串,同时仍然允许第二个参数?
我需要做点什么
str.indexOf(/[abc]/ , i);
和
str.lastIndexOf(/[abc]/ , i);
虽然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)]) ; } }
我正在测试如下以确保至少对于一个字符regexp,结果与我们使用indexOf时相同
//在xes
test('xxx')中查找a ;
试验( 'AXX');
试验( 'XAX');
试验( 'XXA');
试验( 'AXA');
试验( '的Xaa');
试验( 'AAX');
试验( 'AAA');
所述的实例String
构造有一个.search()
方法,它接受一个正则表达式并返回第一匹配的索引.
要从特定位置开始搜索(伪造第二个参数.indexOf()
),您可以slice
关闭第一个i
字符:
str.slice(i).search(/re/)
但是这将得到较短的字符串中的索引(在第一部分被切掉之后),所以你想要将截断的part(i
)的长度添加到返回的索引(如果不是)-1
.这将为您提供原始字符串中的索引:
function regexIndexOf(text, re, i) {
var indexInSuffix = text.slice(i).search(re);
return indexInSuffix < 0 ? indexInSuffix : indexInSuffix + i;
}
结合已经提到的一些方法(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; }
显然,修改内置String对象会为大多数人发送红色标记,但这可能是一次没有那么大的交易; 只是意识到这一点.
更新:编辑regexLastIndexOf()
,这似乎lastIndexOf()
现在模仿.如果仍然失败并在什么情况下,请告诉我.
更新:通过本页评论中找到的所有测试,以及我自己的测试.当然,这并不意味着它是防弹的.任何反馈意见.
我有一个简短的版本.这对我来说很有效!
var match = str.match(/[abc]/gi); var firstIndex = str.indexOf(match[0]); var lastIndex = str.lastIndexOf(match[match.length-1]);
如果你想要一个原型版本:
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; }
编辑:如果你想添加对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; }
要使用它,就像这样简单:
var firstIndex = str.indexOfRegex(/[abc]/gi); var lastIndex = str.lastIndexOfRegex(/[abc]/gi);
使用:
str.search(regex)
请参阅此处的文档.
根据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; }
你可以使用substr.
str.substr(i).match(/[abc]/);
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
您也可以将函数原型制作到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
关于如何修改的快速说明RegExp
:因为indexOf
我只需要确保设置了全局标志即可。对于lastIndexOf
,我正在使用否定的超前查找来查找最后一次出现,除非RegExp
该字符串已在字符串末尾匹配。