簡體   English   中英

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

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

我試圖找到一個字符串在另一個字符串中所有出現的位置,不區分大小寫。

例如,給定字符串:

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>

這是正則表達式免費版本:

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() 的結果對象,我所有的 Javascript 資源都掩蓋了它的使用(除了給你匹配的字符串)。 所以當他使用result.index ,這是某種未命名的匹配對象。 execMDC 描述中,他們實際上詳細地描述了這個對象。

一個使用String.protype.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循環運行它進行一百萬次迭代(使用您的字符串)。 一個班輪平均 1420 毫秒,而for...of在我的機器上平均為 1150 毫秒。 這不是一個微不足道的區別,但是如果您只進行少量比賽,則一個襯墊可以正常工作。

matchAll上caniuse

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

 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函數。

我參加聚會有點晚了(將近 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

這是一個簡單的代碼片段:

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

我會推薦蒂姆的答案。 但是,@blazs 的此評論指出“假設searchStr=aaa和那個str=aaaaaa 。然后,您的代碼將只找到 2 次,而不是找到 4 次,因為您在循環中通過 searchStr.length 進行了跳過。”,這是真的通過查看 Tim 的代碼,特別是這里的這一行: startIndex = index + searchStrLen; 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>

如果我有aaaaaa的 str 和aaa的 searchStr ,將其更改為+ 1而不是+ searchStrLen將允許索引 1 位於索引數組中。

PS 如果有人想在代碼中添加注釋來解釋代碼的工作原理,請說出來,我很樂意回復請求。

按照@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;
}

感謝所有的答復。 我瀏覽了所有這些,並提出了一個函數,該函數為每個出現的 'needle' substring 提供第一個最后一個索引。 我把它貼在這里以防它對某人有幫助。

請注意,它與僅在每次出現的開頭的原始請求不同。 它更適合我的用例,因為您不需要保持針的長度。

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
}

檢查此解決方案,它也可以找到相同的字符串,如果缺少某些內容或不正確,請告訴我。

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

這是我的代碼(使用搜索和切片方法)

 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 和輔助方法查找匹配短語索引的另一種方法。 當然,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'));
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']

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM