JavaScript에서 다른 문자열의 모든 발생 색인을 찾는 방법은 무엇입니까?


105

대소 문자를 구분하지 않는 다른 문자열에서 문자열의 모든 발생 위치를 찾으려고합니다.

예를 들어 다음과 같은 문자열이 있습니다.

레바논에서 우쿨렐레를 배웠습니다.

및 검색 문자열 le, 배열을 얻고 싶습니다.

[2, 25, 27, 33]

두 문자열 모두 변수가됩니다. 즉, 값을 하드 코딩 할 수 없습니다.

정규 표현식으로는 쉬운 일이라고 생각했지만, 작동하는 것을 찾기 위해 한동안 고생 한 끝에 운이 없었습니다.

를 사용하여이 작업을 수행하는 방법에 대한 이 예제 를 찾았 .indexOf()지만 확실히 더 간결한 방법이 있어야합니까?

답변:


165
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>


2
어떻게 할 le변수 문자열이 여기에? 예를 들어 new Regexp(str);특수 문자를 사용 하는 경우에도 위험이 숨어 $2.50있습니다. 좀 regex = new Regexp(dynamicstring.replace(/([\\.+*?\\[^\\]$(){}=!<>|:])/g, '\\$1'));더 가까운 IMHO가 될 것입니다. js에 정규식 이스케이프 메커니즘이 내장되어 있는지 확실하지 않습니다.
Wrikken 2010 년

new RegExp(searchStr)예, 일반적인 경우에는 특수 문자를 이스케이프해야합니다. 그런 수준의 일반성이 필요하지 않으면 실제로 할 가치가 없습니다.
Tim Down

1
훌륭한 답변이며 매우 도움이됩니다. 감사합니다, 팀!
Bungle

1
검색 문자열이 빈 문자열이면 무한 루프가 발생합니다.
HelpMeStackOverflowMyOnlyHope

2
가정 searchStr=aaa하고 str=aaaaaa. 그런 다음 searchStr.length루프에서 건너 뛰기 때문에 코드에서 4 개의 발생을 찾는 대신 2 개만 찾습니다 .
blazs

18

다음은 정규식 무료 버전입니다.

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

7
+1. Regex를 사용하는 솔루션과 비교하기 위해 몇 가지 테스트를 실행했습니다. 가장 빠른 방법은 Regex를 사용하는 방법이었습니다. jsperf.com/javascript-find-all
StuR

1
가장 빠른 방법은 같이 IndexOf를 사용 jsperf.com/find-o-substrings을
에단 Yanjia 리

@LiEthan 해당 함수가 병목 상태이고 입력 문자열이 긴 경우에만 중요합니다.
jcubic

@jcubic 귀하의 솔루션은 좋아 보이지만 약간의 혼란이 있습니다. 이렇게 함수를 호출하면 var result = indexes('aaaa', 'aa')어떻게 되나요? 예상 결과는 [0, 1, 2]또는 [0, 2]?
Cao Mạnh Quang

@ CaoMạnhQuang이 코드를 보면 첫 번째 결과입니다. 두 번째 하나를 원하는 경우에 당신은 루프 내부 당신이 세우면 동안 작성해야 i+=find.length;하고 다른 사람에i++
jcubic

15

당신은 확실히 할 수 있습니다!

//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그것은 일종의 이름없는 Match Object입니다. execMDC 설명에서 실제로이 개체를 상당히 자세하게 설명합니다.


하아! 기여해 주셔서 감사합니다. 어쨌든 감사합니다!
Bungle

9

String.protype.matchAll(ES2020)을 사용하는 라이너 1 개 :

[...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(당신의 문자열을 사용하여) 백만 번의 반복을위한 루프로 그것을 실행했습니다 . 하나의 라이너는 평균 1420ms이고 for...of내 컴퓨터 의 평균은 1150ms입니다. 그것은 사소한 차이는 아니지만 몇 번의 성냥 만한다면 한 라이너가 잘 작동합니다.

참조 matchAllcaniuse에


3

모든 경기의 위치를 ​​찾고 싶다면 약간의 해킹을 알려 드리고자합니다.

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이전 기능을 사용하십시오 .


RegExp를 사용하는 것은 위험하기 때문에 귀하의 대답이 가장 좋은 것이라고 생각합니다.
Bharata

1

다음은 간단한 코드입니다.

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


0

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

0

모든 답변에 감사드립니다. 나는 그들 모두를 살펴보고 'needle'부분 문자열의 각 발생에 대한 마지막 색인을 처음에 제공하는 함수를 생각해 냈습니다. 누군가에게 도움이 될 수 있도록 여기에 게시하고 있습니다.

각 발생의 시작에 대한 원래 요청과 동일하지 않습니다. 바늘 길이를 유지할 필요가 없기 때문에 내 사용 사례에 더 적합합니다.

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
}

0

동일한 문자열을 찾을 수있는이 솔루션을 확인하고 누락 된 것이 있는지 아닌지 알려주십시오.

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


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

}

이것은 정규식이 아닌 다른 문자열 내에서 문자열의 발생을 찾습니다.

-1

아래 코드가 작업을 수행합니다.

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

-2

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

이것은 매우 간단합니다.
igaurav

11
문제는 자신이 발생하는 것이 아니라 발생의 인덱스 를 찾는 방법입니다 !
Luckylooke 2017

1
질문과 일치 나던이 답변을 견디는하지만 내가 :) 무엇을 찾고 있었다입니다
AlexNikonov
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.