如何在 JavaScript 中查找一个字符串在另一个字符串中的所有匹配项的索引?

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

例如,给定字符串:

I learned to play the Ukulele in Lebanon.

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

[2, 25, 27, 33]

这两个字符串都是变量-也就是说,我不能硬编码它们的值。

我认为对于正则表达式来说,这是一项很容易的任务,但是经过一段时间的努力,我找到了一个可行的方法,但是一直没有成功。

我发现 这个例子如何实现这一点使用 .indexOf(),但肯定有一个更简洁的方式来做到这一点?

192212 次浏览

以下是不含正则表达式的版本:

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")

编辑 : 如果你想匹配像‘ 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;
}

你肯定能做到!

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

编辑: 学习拼写 RegExp

同时,我意识到这不是你想要的 没错,因为 lastIndex告诉我们指针的末端不是开始,但它很接近-你可以把 re.lastIndex-needle.length推入结果数组..。

编辑: 添加链接

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

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

更新

在最初的问题中,我没有发现搜索字符串需要是一个变量。我已经写了另一个版本来处理这个案件,使用 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>

function countInString(searchFor,searchIn){


var results=0;
var a=searchIn.indexOf(searchFor)


while(a!=-1){
searchIn=searchIn.slice(a*1+searchFor.length);
results++;
a=searchIn.indexOf(searchFor);
}


return results;


}

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

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")

使用 String.Prototype.match

下面是 MDN 文档本身的一个例子:

var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
var regexp = /[A-E]/gi;
var matches_array = str.match(regexp);


console.log(matches_array);
// ['A', 'B', 'C', 'D', 'E', 'a', 'b', 'c', 'd', 'e']

如果你只是想找出所有匹配的位置,我想给你指出一个小窍门:

var 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); // {pos: ...} – Object wich is used as this


console.log(splitOnFound);

如果您有一个长度可变的 RegExp,那么它可能不适用,但是对于某些人来说,它可能是有帮助的。

这是区分大小写的。对于不区分大小写的,在使用 String.toLowerCase函数之前。

按照@jsquare 的回答,他的解决方案给我的案子带来了一点小小的困惑
例如,var result = indexes('aaaa', 'aa')将返回 [0, 1, 2]而不是 [0, 2]
因此,我更新了一下他的解决方案,如下所示,以匹配我的情况 < br/>

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

下面是一个简单的代码片段:

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, []));

谢谢你的回复。我浏览了所有内容,得到了一个函数,它为第一个子字符串的每个匹配项提供了最后一个索引。我把它贴在这里,以防它会帮助别人。

请注意,这不是相同的原始请求,只有开始的每个事件。它更适合我的用例,因为你不需要保持针的长度。

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
}

使用 String.prototype.matchAll(ES2020)的一个班轮:

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

运用你的价值观:

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]

如果您担心在一行中执行传播和 map(),那么我使用 for...of循环(使用字符串)进行了100万次迭代。一个班轮平均1420毫秒,而 for...of平均1150毫秒在我的机器。这并不是一个无关紧要的差异,但是如果您只做少量匹配,那么一行代码就可以很好地工作。

请参阅犬的 matchAll

检查这个解决方案,将能够找到相同的字符串太,让我知道是否有东西缺失或不正确。

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@blazs 声明“假设 searchStr=aaastr=aaaaaa。然后你的代码不会找到4次,而只会找到2次,因为你在循环中跳过了 searchStr.length。”,通过查看 Tim 的代码,尤其是这一行: ,Tim 的代码将无法找到正在搜索的字符串的一个实例,这个实例在它自己的长度范围内。所以,我修改了 Tim 的回答:

function getIndicesOf(searchStr, str, caseSensitive) {
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 + 1;
}
return indices;
}
var searchStr = prompt("Enter a string.");
var str = prompt("What do you want to search for in the string?");
var indices = getIndicesOf(str, searchStr);


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

Changing it to + 1 instead of + searchStrLen will allow the index 1 to be in the indices array if I have an str of aaaaaa and a searchStr of aaa.

P.S. If anyone would like comments in the code to explain how the code works, please say so, and I'll be happy to respond to the request.

我有点晚了党(差不多10年,2个月) ,但一个方法为未来的编码器是使用 while 循环和 indexOf()

let haystack = "I learned to play the Ukulele in Lebanon.";
let needle = "le";
let pos = 0; // Position Ref
let result = []; // Final output of all index's.
let hayStackLower = haystack.toLowerCase();


// Loop to check all occurrences
while (hayStackLower.indexOf(needle, pos) != -1) {
result.push(hayStackLower.indexOf(needle , pos));
pos = hayStackLower.indexOf(needle , pos) + 1;
}


console.log("Final ", result); // Returns all indexes or empty array if not found

下面是我的代码(使用搜索和切片方法)

    let s = "I learned to play the Ukulele in Lebanon"
let sub = 0
let matchingIndex = []
let index = s.search(/le/i)
while( index >= 0 ){
matchingIndex.push(index+sub);
sub = sub + ( s.length - s.slice( index+1 ).length )
s = s.slice( index+1 )
index = s.search(/le/i)
}
console.log(matchingIndex)

这是我通常用来获得一个字符串索引也根据它的位置。

我传递以下参数:

Search : 要搜索的字符串

Find : 要查找的字符串

Position (默认情况下为‘ all’) : 查找字符串在搜索字符串中出现的位置

(如果‘ all’返回索引的完整数组)

(如果‘ last’返回最后一个位置)

function stringIndex (search, find, position = "all") {
    

var currIndex = 0, indexes = [], found = true;
    

while (found) {
var searchIndex = search.indexOf(find);
if (searchIndex > -1) {
currIndex += searchIndex + find.length;
search = search.substr (searchIndex + find.length);
indexes.push (currIndex - find.length);
} else found = false; //no other string to search for - exit from while loop
}
    

if (position == 'all') return indexes;
if (position > indexes.length -1) return [];
    

position = (position == "last") ? indexes.length -1 : position;
    

return indexes[position];
}


//Example:
    

var myString = "Joe meets Joe and together they go to Joe's house";
console.log ( stringIndex(myString, "Joe") ); //0, 10, 38
console.log ( stringIndex(myString, "Joe", 1) ); //10
console.log ( stringIndex(myString, "Joe", "last") ); //38
console.log ( stringIndex(myString, "Joe", 5) ); //[]

嗨,朋友们,这只是使用 reduce 和 helper 方法查找匹配短语索引的另一种方法。当然,RegExp 更方便,而且可能是以这种方式在内部实现的。希望对你有用。

function findIndexesOfPhraseWithReduce(text, phrase) {
//convert text to array so that be able to manipulate.
const arrayOfText = [...text];


/* this function takes the array of characters and
the search phrase and start index which comes from reduce method
and calculates the end with length of the given phrase then slices
and joins characters and compare it whith phrase.
and returns True Or False */


function isMatch(array, phrase, start) {
const end = start + phrase.length;
return (array.slice(start, end).join('')).toLowerCase() ===
phrase.toLowerCase();
}


/* here we reduce the array of characters and test each character
with isMach function which takes "current index" and matches the phrase
with the subsequent character which starts from current index and
ends at the last character of phrase(the length of phrase). */


return arrayOfText.reduce((acc, item, index) => isMatch(arrayOfText, phrase,
index) ? [...acc, index] : acc, []);
}


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

function findIndexesOfPhraseWithReduce(text, phrase) {
     

const arrayOfText = [...text];
function isMatch(array, phrase, start) {
const end = start + phrase.length;
return (array.slice(start, end).join('')).toLowerCase() ===
phrase.toLowerCase();
}
return arrayOfText.reduce((acc, item, index) => isMatch(arrayOfText, phrase,
index) ? [...acc, index] : acc, []);
}


console.log(findIndexesOfPhraseWithReduce("I learned to play the Ukulele in Lebanon.", "le"));

const findAllOccurrences = (str, substr) => {
str = str.toLowerCase();
  

let result = [];


let idx = str.indexOf(substr)
  

while (idx !== -1) {
result.push(idx);
idx = str.indexOf(substr, idx+1);
}
return result;
}


console.log(findAllOccurrences('I learned to play the Ukulele in Lebanon', 'le'));