例如,给定字符串:
I learned to play the Ukulele in Lebanon.和搜索字符串
le
,我想要得到数组:[2, 25, 27, 33]
两个字符串都将是变量 - 即不能硬编码它们的值。
我认为这对于正则表达式来说应该很容易,但是在苦苦挣扎一段时间后,我没有找到可行的解决方法。
我发现了这个示例,展示了如何使用.indexOf()
实现,但肯定有更简洁的方法吧?
I learned to play the Ukulele in Lebanon.和搜索字符串
le
,我想要得到数组:[2, 25, 27, 33]
两个字符串都将是变量 - 即不能硬编码它们的值。
我认为这对于正则表达式来说应该很容易,但是在苦苦挣扎一段时间后,我没有找到可行的解决方法。
我发现了这个示例,展示了如何使用.indexOf()
实现,但肯定有更简洁的方法吧?
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>
使用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]
for...of
循环对使用您的字符串进行了100万次迭代。这个一行代码的平均时间是1420ms,而for...of
的平均时间是1150ms。这不是一个微不足道的差异,但如果你只需要做几个匹配,一行代码就可以正常工作。
请参见caniuse上的matchAll
这是一个不需要正则表达式的版本:
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;
}
i+=find.length;
代替if语句,否则使用i++
。 - jcubic你肯定可以做到这件事!
//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);
}
编辑:学会拼写正则表达式
另外,我意识到这并不完全是你想要的,因为lastIndex
告诉我们针的结尾而不是开头,但它很接近——你可以把 re.lastIndex-needle.length
推入结果数组中...
编辑:添加链接
@Tim Down的答案使用了RegExp.exec()的results对象,而我所有的JavaScript资源都忽略了它的用法(除了给出匹配的字符串)。所以当他使用result.index
时,那是某种未命名的Match Object。在MDC exec描述中,他们实际上很详细地描述了这个对象。
我有点晚(差不多晚了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
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'));
如果您只想查找所有匹配项的位置,我想给您指一条小技巧:
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);
如果您使用的是具有可变长度的正则表达式,则可能不适用,但对于某些人来说可能会有所帮助。
此处区分大小写。如需不区分大小写,请在使用String.toLowerCase
函数之前进行转换。
I would recommend Tim's answer. However, this comment by @blazs states "Suppose searchStr=aaa
and that str=aaaaaa
. Then instead of finding 4 occurences your code will find only 2 because you're making skips by searchStr.length in the loop.", which is true by looking at Tim's code, specifically this line here: startIndex = index + searchStrLen;
Tim's code would not be able to find an instance of the string that's being searched that is within the length of itself. So, I've modified Tim's answer:
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>
+ 1
而不是+ searchStrLen
,如果我有一个字符串aaaaaa
和一个搜索字符串aaa
,则允许索引1在索引数组中。这是我通常用来根据位置获取字符串索引的方法。
我传递以下参数:
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和辅助方法查找匹配短语索引的另一种方式。当然,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"));
searchStr=aaa
,并且str=aaaaaa
。那么你的代码只能找到2个匹配项,而不是4个,因为在循环中你正在跳过searchStr.length
。 - blazs