[javascript] 전역 플래그가있는 RegExp가 왜 잘못된 결과를 제공합니까?

전역 플래그와 대소 문자를 구분하지 않는 플래그를 사용할 때이 정규식의 문제점은 무엇입니까? 쿼리는 사용자 생성 입력입니다. 결과는 [true, true] 여야합니다.

var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));
result.push(re.test('Foo Bar'));
// result will be [true, false]

var reg = /^a$/g;
for(i = 0; i++ < 10;)
   console.log(reg.test("a"));



답변

RegExp객체의 추적 lastIndex일치가 발생한 위치 때문에 다음 경기에가보세요 0 대신, 마지막으로 사용 된 인덱스에서 시작됩니다 :

var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));

alert(re.lastIndex);

result.push(re.test('Foo Bar'));

lastIndex모든 테스트 후에 수동으로 0 으로 재설정하지 않으려면 g플래그를 제거하십시오 .

스펙이 지시하는 알고리즘은 다음과 같습니다 (섹션 15.10.6.2).

RegExp.prototype.exec (문자열)

정규식에 대해 string의 정규식 일치를 수행하고 일치 결과를 포함하는 Array 객체를 반환하거나 문자열이 일치하지 않으면 null을 반환합니다. ToString (string) 문자열은 다음과 같이 정규식 패턴의 발생을 검색합니다.

  1. S를 ToString (string)의 값으로 설정하십시오.
  2. length를 S의 길이라고하자.
  3. lastIndex를 lastIndex 속성 값으로 설정하십시오.
  4. 내가 ToInteger (lastIndex)의 값이라고합시다.
  5. 전역 속성이 false이면 i = 0으로 설정하십시오.
  6. I <0 또는 I> length이면 lastIndex를 0으로 설정하고 null을 반환합니다.
  7. [[Match]]를 호출하여 인수 S와 i를 지정하십시오. [[Match]]가 실패를 반환하면 8 단계로 이동하십시오. 그렇지 않으면 r을 State 결과로 설정하고 10 단계로 이동하십시오.
  8. i = i + 1이라고하자.
  9. 6 단계로 이동하십시오.
  10. e를 r의 endIndex 값으로하자.
  11. 전역 속성이 true이면 lastIndex를 e로 설정하십시오.
  12. n을 r의 캡처 배열의 길이라고하자. (15.10.2.1의 NCapturingParens와 같은 값입니다.)
  13. 다음 속성을 가진 새 배열을 반환하십시오.
    • 인덱스 속성은 전체 문자열 S 내에서 일치하는 하위 문자열의 위치로 설정됩니다.
    • 입력 특성이 S로 설정되어 있습니다.
    • 길이 속성은 n + 1로 설정되어 있습니다.
    • 0 속성은 일치하는 부분 문자열로 설정됩니다 (즉, 오프셋 i와 오프셋 e 사이의 S 부분).
    • I> 0 및 I ≤ n 인 각 정수 i에 대해 ToString (i)이라는 특성을 r의 캡처 배열의 i 번째 요소로 설정하십시오.

답변

단일 RegExp객체를 사용하고 여러 번 실행 중입니다. 각 연속적인 실행에서 마지막 일치 색인부터 계속됩니다.

각 실행 전에 처음부터 시작하려면 정규식을 “재설정”해야합니다.

result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));
// result is now [true, true]

매번 새로운 RegExp 객체를 생성하는 것이 더 읽기 쉬울 수 있다고 말했지만 (RegExp가 캐싱되므로 오버 헤드는 최소화됩니다) :

result.push((/Foo B/gi).test(stringA));
result.push((/Foo B/gi).test(stringB));


답변

RegExp.prototype.test정규식의 lastIndex속성을 업데이트하여 각 테스트가 마지막 테스트가 중지 된 위치에서 시작되도록합니다. 속성을 String.prototype.match업데이트하지 않기 때문에 사용하는 것이 좋습니다 lastIndex.

!!'Foo Bar'.match(re); // -> true
!!'Foo Bar'.match(re); // -> true

참고 : !!이를 부울로 변환 한 다음 부울을 뒤집어 결과를 반영합니다.

또는 lastIndex속성을 재설정 할 수도 있습니다.

result.push(re.test('Foo Bar'));
re.lastIndex = 0;
result.push(re.test('Foo Bar'));


답변

전역 g플래그를 제거 하면 문제가 해결됩니다.

var re = new RegExp(query, 'gi');

해야한다

var re = new RegExp(query, 'i');


답변

g 플래그 regex를 사용하면 마지막 일치 항목을 추적하기 때문에 re.lastIndex = 0을 설정해야하므로 테스트는 동일한 문자열을 테스트하지 않으므로 re.lastIndex = 0을 수행해야합니다.

var query = 'Foo B';
var re = new RegExp(query, 'gi');
var result = [];
result.push(re.test('Foo Bar'));
re.lastIndex=0;
result.push(re.test('Foo Bar'));

console.log(result)


답변

/ g 플래그를 사용하면 적중 후 검색을 계속하도록 지시합니다.

일치하는 경우 exec () 메서드는 배열을 반환하고 정규식 객체의 속성을 업데이트합니다.

처음 검색하기 전에 :

myRegex.lastIndex
//is 0

첫 검색 후

myRegex.lastIndex
//is 8

g를 제거하면 exec ()를 호출 할 때마다 검색을 종료합니다.


답변

나는 기능을했다 :

function parseDevName(name) {
  var re = /^([^-]+)-([^-]+)-([^-]+)$/g;
  var match = re.exec(name);
  return match.slice(1,4);
}

var rv = parseDevName("BR-H-01");
rv = parseDevName("BR-H-01");

첫 번째 전화가 작동합니다. 두 번째 전화는 그렇지 않습니다. slice작업은 널 (null) 값에 대해 불평. 나는 이것이 때문이라고 가정합니다 re.lastIndex. RegExp함수가 호출 될 때마다 새로운 함수가 할당되고 함수를 여러 번 호출 할 때 공유되지 않기 때문에 이상 합니다.

내가 그것을 변경했을 때 :

var re = new RegExp('^([^-]+)-([^-]+)-([^-]+)$', 'g');

그런 다음 lastIndex홀드 오버 효과를 얻지 못합니다 . 예상대로 작동합니다.