如何在 JavaScript 中查找一个字符串在另一个字符串中所有出现的索引?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3410464/
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-08-23 04:31:18  来源:igfitidea点击:

How to find indices of all occurrences of one string in another in JavaScript?

javascriptregexstringindexof

提问by Bungle

I'm trying to find the positions of all occurrences of a string in another string, case-insensitive.

我试图在另一个字符串中找到所有出现的字符串的位置,不区分大小写。

For example, given the string:

例如,给定字符串:

I learned to play the Ukulele in Lebanon.

and the search string le, I want to obtain the array:

和搜索字符串le,我想获取数组:

[2, 25, 27, 33]

Both strings will be variables - i.e., I can't hard-code their values.

两个字符串都是变量——即,我不能对它们的值进行硬编码。

I figured that this was an easy task for regular expressions, but after struggling for a while to find one that would work, I've had no luck.

我认为这对于正则表达式来说是一项简单的任务,但是在努力寻找可以工作的一段时间之后,我没有走运。

I found this exampleof how to accomplish this using .indexOf(), but surely there has to be a more concise way to do it?

我找到了如何使用 完成此操作的示例.indexOf(),但肯定必须有更简洁的方法来做到这一点?

回答by Tim Down

var str = "I learned to play the Ukulele in Lebanon."
var regex = /le/gi, result, indices = [];
while ( (result = regex.exec(str)) ) {
    indices.push(result.index);
}

UPDATE

更新

I failed to spot in the original question that the search string needs to be a variable. I've written another version to deal with this case that uses indexOf, so you're back to where you started. As pointed out by Wrikken in the comments, to do this for the general case with regular expressions you would need to escape special regex characters, at which point I think the regex solution becomes more of a headache than it's worth.

我未能在原始问题中发现搜索字符串需要是一个变量。我写了另一个版本来处理这个使用 的情况indexOf,所以你又回到了开始的地方。正如 Wrikken 在评论中指出的那样,要在使用正则表达式的一般情况下执行此操作,您需要转义特殊的正则表达式字符,此时我认为正则表达式解决方案变得更加令人头疼,而不是值得。

function getIndicesOf(searchStr, str, caseSensitive) {
    var searchStrLen = searchStr.length;
    if (searchStrLen == 0) {
        return [];
    }
    var startIndex = 0, index, indices = [];
    if (!caseSensitive) {
        str = str.toLowerCase();
        searchStr = searchStr.toLowerCase();
    }
    while ((index = str.indexOf(searchStr, startIndex)) > -1) {
        indices.push(index);
        startIndex = index + searchStrLen;
    }
    return indices;
}

var indices = getIndicesOf("le", "I learned to play the Ukulele in Lebanon.");

document.getElementById("output").innerHTML = indices + "";
<div id="output"></div>

回答by jcubic

Here is regex free version:

这是正则表达式免费版本:

function indexes(source, find) {
  if (!source) {
    return [];
  }
  // if find is empty string return all indexes.
  if (!find) {
    // or shorter arrow function:
    // return source.split('').map((_,i) => i);
    return source.split('').map(function(_, i) { return i; });
  }
  var result = [];
  for (i = 0; i < source.length; ++i) {
    // If you want to search case insensitive use 
    // if (source.substring(i, i + find.length).toLowerCase() == find) {
    if (source.substring(i, i + find.length) == find) {
      result.push(i);
    }
  }
  return result;
}

indexes("I learned to play the Ukulele in Lebanon.", "le")

EDIT: and if you want to match strings like 'aaaa' and 'aa' to find [0, 2] use this version:

编辑:如果你想匹配像 'aaaa' 和 'aa' 这样的字符串来找到 [0, 2] 使用这个版本:

function indexes(source, find) {
  if (!source) {
    return [];
  }
  if (!find) {
      return source.split('').map(function(_, i) { return i; });
  }
  var result = [];
  var i = 0;
  while(i < source.length) {
    if (source.substring(i, i + find.length) == find) {
      result.push(i);
      i += find.length;
    } else {
      i++;
    }
  }
  return result;
}

回答by Ryley

You sure can do this!

你一定能做到!

//make a regular expression out of your needle
var needle = 'le'
var re = new RegExp(needle,'gi');
var haystack = 'I learned to play the Ukulele';

var results = new Array();//this is the results you want
while (re.exec(haystack)){
  results.push(re.lastIndex);
}

Edit: learn to spell RegExp

编辑:学习拼写 RegExp

Also, I realized this isn't exactlywhat you want, as lastIndextells us the end of the needle not the beginning, but it's close - you could push re.lastIndex-needle.lengthinto the results array...

此外,我意识到这不是正是你想要什么,lastIndex告诉我们针不是开始的结束,但它很接近-你可以推re.lastIndex-needle.length到结果数组...

Edit: adding link

编辑:添加链接

@Tim Down's answer uses the results object from RegExp.exec(), and all my Javascript resources gloss over its use (apart from giving you the matched string). So when he uses result.index, that's some sort of unnamed Match Object. In the MDC description of exec, they actually describe this object in decent detail.

@Tim Down 的回答使用来自 RegExp.exec() 的结果对象,我所有的 Javascript 资源都掩盖了它的使用(除了给你匹配的字符串)。所以当他使用 时result.index,那是某种未命名的匹配对象。在execMDC 描述中,他们实际上详细地描述了这个对象。

回答by Benny Hinrichs

One liner using String.protype.matchAll(ES2020):

一个衬垫使用String.protype.matchAll(ES2020):

[...sourceStr.matchAll(new RegExp(searchStr, 'gi'))].map(a => a.index)

Using your values:

使用你的价值观:

const sourceStr = 'I learned to play the Ukulele in Lebanon.';
const searchStr = 'le';
const indexes = [...sourceStr.matchAll(new RegExp(searchStr, 'gi'))].map(a => a.index);
console.log(indexes); // [2, 25, 27, 33]

If you're worried about doing a spread and a map()in one line, I ran it with a for...ofloop for a million iterations (using your strings). The one liner averages 1420ms while the for...ofaverages 1150ms on my machine. That's not an insignificant difference, but the one liner will work fine if you're only doing a handful of matches.

如果您担心map()在一行中进行传播和 a ,我会用for...of循环运行它进行一百万次迭代(使用您的字符串)。一个班轮平均为 1420 毫秒,而for...of在我的机器上平均为 1150 毫秒。这不是一个微不足道的区别,但是如果您只进行少量比赛,则一个衬垫可以正常工作。

See matchAllon caniuse

请参见matchAll上caniuse

回答by Hoffmann

If you just want to find the position of all matches I'd like to point you to a little hack:

如果您只想找到所有匹配项的位置,我想向您指出一个小技巧:

haystack = 'I learned to play the Ukulele in Lebanon.'
needle = 'le'
splitOnFound = haystack.split(needle).map(function (culm) {
  return this.pos += culm.length + needle.length
}, {pos: -needle.length}).slice(0, -1)

it might not be applikable if you have a RegExp with variable length but for some it might be helpful.

如果您有一个长度可变的 RegExp,它可能不适用,但对于某些它可能会有所帮助。

回答by Kapil Tiwari

Here is a simple Code

这是一个简单的代码

function getIndexOfSubStr(str, searchToken, preIndex, output){
   var result = str.match(searchToken);
     if(result){
     output.push(result.index +preIndex);
     str=str.substring(result.index+searchToken.length);
     getIndexOfSubStr(str, searchToken, preIndex, output)
     }
     return output;
  };

var str = "my name is 'xyz' and my school name is 'xyz' and my area name is 'xyz' ";
var  searchToken ="my";
var preIndex = 0;

console.log(getIndexOfSubStr(str, searchToken, preIndex, []));

回答by Roei Bahumi

Thanks for all the replies. I went through all of them and came up with a function that gives the first an last index of each occurrence of the 'needle' substring . I am posting it here in case it will help someone.

感谢所有的答复。我浏览了所有这些,并提出了一个函数,该函数为每个出现的 'needle' substring 提供第一个最后一个索引。我把它贴在这里以防它对某人有帮助。

Please note, it is not the same as the original request for only the beginning of each occurrence. It suits my usecase better because you don't need to keep the needle length.

请注意,它与仅在每次出现的开头的原始请求不同。它更适合我的用例,因为您不需要保持针的长度。

function findRegexIndices(text, needle, caseSensitive){
  var needleLen = needle.length,
    reg = new RegExp(needle, caseSensitive ? 'gi' : 'g'),
    indices = [],
    result;

  while ( (result = reg.exec(text)) ) {
    indices.push([result.index, result.index + needleLen]);
  }
  return indices
}

回答by Jignesh Sanghani

Check this solution which will able to find same character string too, let me know if something missing or not right.

检查此解决方案,它也可以找到相同的字符串,如果缺少某些内容或不正确,请告诉我。

function indexes(source, find) {
    if (!source) {
      return [];
    }
    if (!find) {
        return source.split('').map(function(_, i) { return i; });
    }
    source = source.toLowerCase();
    find = find.toLowerCase();
    var result = [];
    var i = 0;
    while(i < source.length) {
      if (source.substring(i, i + find.length) == find)
        result.push(i++);
      else
        i++
    }
    return result;
  }
  console.log(indexes('aaaaaaaa', 'aaaaaa'))
  console.log(indexes('aeeaaaaadjfhfnaaaaadjddjaa', 'aaaa'))
  console.log(indexes('wordgoodwordgoodgoodbestword', 'wordgood'))
  console.log(indexes('I learned to play the Ukulele in Lebanon.', 'le'))

回答by Cao M?nh Quang

Follow the answer of @jcubic, his solution caused a small confuse for my case
For example var result = indexes('aaaa', 'aa')it will return [0, 1, 2]instead of [0, 2]
So I updated a bit his solution as below to match my case

按照@jcubic 的回答,他的解决方案对我的案例造成了一些混乱
例如var result = indexes('aaaa', 'aa')它将返回[0, 1, 2]而不是[0, 2]
所以我更新了他的解决方案如下以匹配我的案例

function indexes(text, subText, caseSensitive) {
    var _source = text;
    var _find = subText;
    if (caseSensitive != true) {
        _source = _source.toLowerCase();
        _find = _find.toLowerCase();
    }
    var result = [];
    for (var i = 0; i < _source.length;) {
        if (_source.substring(i, i + _find.length) == _find) {
            result.push(i);
            i += _find.length;  // found a subText, skip to next position
        } else {
            i += 1;
        }
    }
    return result;
}

回答by G.Nader

the below code will do the job for you :

下面的代码将为您完成这项工作:

function indexes(source, find) {
  var result = [];
  for(i=0;i<str.length; ++i) {
    // If you want to search case insensitive use 
    // if (source.substring(i, i + find.length).toLowerCase() == find) {
    if (source.substring(i, i + find.length) == find) {
      result.push(i);
    }
  }
  return result;
}

indexes("hello, how are you", "ar")