JavaScript: String.indexOf(...) 允许使用正则表达式吗?

277
在JavaScript中,是否有一个等价于String.indexOf(...)的方法,它可以接受一个正则表达式作为第一个参数,同时还允许传入第二个参数?
我需要做类似的操作。
str.indexOf(/[abc]/ , i);

并且

str.lastIndexOf(/[abc]/ , i);

虽然String.search()接受一个正则表达式作为参数,但它不允许我指定第二个参数!
编辑:
这比我最初想象的要难,所以我编写了一个小的测试函数来测试所有提供的解决方案...它假设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)]) ;
    }
}

我正在进行以下测试,以确保至少对于一个字符的正则表达式,结果与使用indexOf相同。
//在一堆x中寻找a test('xxx'); test('axx'); test('xax'); test('xxa'); test('axa'); test('xaa'); test('aax'); test('aaa');

在编程中,[ ] 中的 | 表示字面字符 |。你可能意思是 [abc] - Markus Jarderot
是的,谢谢你,你说得对,我会修复它,但正则表达式本身并不重要... - Pat
我发现一个更简单有效的方法是只使用string.match(/ [A-Z] /)。如果没有匹配,该方法返回null,否则您将获得一个对象,您可以执行match(/ [A-Z] /)。index以获取第一个大写字母的索引。 - Syler
对于那些对各种解决方案的性能感兴趣的人,请访问以下链接:https://jsperf.app/reguqe/2 - undefined
22个回答

0

对于匹配稀疏的数据,使用string.search在所有浏览器中都是最快的。它会在每次迭代中重新切片字符串以:

function lastIndexOfSearch(string, regex, index) {
  if(index === 0 || index)
     string = string.slice(0, Math.max(0,index));
  var idx;
  var offset = -1;
  while ((idx = string.search(regex)) !== -1) {
    offset += idx + 1;
    string = string.slice(idx + 1);
  }
  return offset;
}

对于密集数据,我制作了这个。与执行方法相比,它更为复杂,但对于密集数据,它比我尝试过的其他方法快2-10倍,比被接受的解决方案快约100倍。主要要点如下:
  1. 它在传递的正则表达式上调用 exec 一次以验证是否有匹配项或提前退出。我使用类似的方法,但在 IE 上使用 exec 检查速度要快得多。
  2. 它构建并缓存了一个修改后的正则表达式,格式为“(r)。(?!.?r)”
  3. 执行新的正则表达式,并返回该 exec 或第一个 exec 的结果;

    function lastIndexOfGroupSimple(string, regex, index) {
        if (index === 0 || index) string = string.slice(0, Math.max(0, index + 1));
        regex.lastIndex = 0;
        var lastRegex, index
        flags = 'g' + (regex.multiline ? 'm' : '') + (regex.ignoreCase ? 'i' : ''),
        key = regex.source + '$' + flags,
        match = regex.exec(string);
        if (!match) return -1;
        if (lastIndexOfGroupSimple.cache === undefined) lastIndexOfGroupSimple.cache = {};
        lastRegex = lastIndexOfGroupSimple.cache[key];
        if (!lastRegex)
            lastIndexOfGroupSimple.cache[key] = lastRegex = new RegExp('.*(' + regex.source + ')(?!.*?' + regex.source + ')', flags);
        index = match.index;
        lastRegex.lastIndex = match.index;
        return (match = lastRegex.exec(string)) ? lastRegex.lastIndex - match[1].length : index;
    };
    

方法的jsPerf

我不理解上面测试的目的。需要正则表达式的情况无法与调用indexOf进行比较,这恰恰是首先创建该方法的目的。为了使测试通过,使用'xxx+(?!x)'比调整正则表达式迭代的方式更合理。


-2

嗯,既然你只是想匹配一个字符的位置,正则表达式可能有点过头了。

我猜你只是想找到这些字符中的第一个,而不是“找到这个字符集合中的第一个字符”。

当然,这是简单的答案,但它可以实现你的问题所要求的功能,尽管没有使用正则表达式(因为你没有说明为什么一定要用正则表达式)。

function mIndexOf( str , chars, offset )
{
   var first  = -1; 
   for( var i = 0; i < chars.length;  i++ )
   {
      var p = str.indexOf( chars[i] , offset ); 
      if( p < first || first === -1 )
      {
           first = p;
      }
   }
   return first; 
}
String.prototype.mIndexOf = function( chars, offset )
{
   return mIndexOf( this, chars, offset ); # I'm really averse to monkey patching.  
};
mIndexOf( "hello world", ['a','o','w'], 0 );
>> 4 
mIndexOf( "hello world", ['a'], 0 );
>> -1 
mIndexOf( "hello world", ['a','o','w'], 4 );
>> 4
mIndexOf( "hello world", ['a','o','w'], 5 );
>> 6
mIndexOf( "hello world", ['a','o','w'], 7 );
>> -1 
mIndexOf( "hello world", ['a','o','w','d'], 7 );
>> 10
mIndexOf( "hello world", ['a','o','w','d'], 10 );
>> 10
mIndexOf( "hello world", ['a','o','w','d'], 11 );
>> -1

关于猴子补丁的评论 - 虽然我知道它的问题,但你认为污染全局命名空间更好吗?这并不是说在这两种情况下符号冲突不会发生,并且如果出现问题,基本上采用相同的方式进行重构/修复。 - Peter Bailey
我需要搜索\s和在某些情况下的\W,希望不必枚举所有可能性。 - Pat
BaileyP:你可以避免全局命名空间污染的问题,例如:看看jQuery的例子。使用那个模型。一个项目对应一个对象,你的东西放在里面。Mootools让我感到不舒服。 - Kent Fredric
还需注意的是,我从未像我在那里写的那样编码。这个例子是为了使用案例简化而简化的。 - Kent Fredric

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接