[javascript] “asdf”.replace (/.*/ g,“x”) ==“xx”인 이유는 무엇입니까?

나는 (나에게) 놀라운 사실을 발견했다.

console.log("asdf".replace(/.*/g, "x"));

두 번 교체해야합니까? 줄 바꿈이없는 비어 있지 않은 문자열은이 패턴을 정확히 두 번 대체하는 것으로 보입니다. 교체 기능을 사용하면 첫 번째 교체가 전체 문자열에 대한 것이고 두 번째 교체는 빈 문자열에 대한 것임을 알 수 있습니다.



답변

당으로 ECMA-262 표준, String.prototype.replace는 호출 RegExp.prototype [대체 @@] 말한다 :

11. Repeat, while done is false
  a. Let result be ? RegExpExec(rx, S).
  b. If result is null, set done to true.
  c. Else result is not null,
    i. Append result to the end of results.
    ii. If global is false, set done to true.
    iii. Else,
      1. Let matchStr be ? ToString(? Get(result, "0")).
      2. If matchStr is the empty String, then
        a. Let thisIndex be ? ToLength(? Get(rx, "lastIndex")).
        b. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).
        c. Perform ? Set(rx, "lastIndex", nextIndex, true).

rx이다 /.*/g하고 S있다 'asdf'.

11.c.iii.2.b 참조 :

비. nextIndex를 AdvanceStringIndex (S, thisIndex, fullUnicode)로 설정하십시오.

따라서 'asdf'.replace(/.*/g, 'x')실제로는 다음과 같습니다.

  1. 결과 (정의되지 않음), 결과 = [], lastIndex =0
  2. 결과 = 'asdf', 결과 = [ 'asdf' ], lastIndex =4
  3. result = '', results = [ 'asdf', '' ], lastIndex = 4,, AdvanceStringIndexlastIndex를 다음으로 설정하십시오.5
  4. 결과 = null, 결과 = [ 'asdf', '' ], 반환

따라서 2 개의 일치 항목이 있습니다.


답변

yawkat 과의 오프라인 채팅에서 두 개의 일치 항목을 정확히 생성 하는 직관적 인 방법 을 찾았 "abcd".replace(/.*/g, "x")습니다. 우리는 그것이 ECMAScript 표준에 의해 부과 된 의미론과 완전히 동일한 지 여부를 확인하지 않았으므로, 단지 경험의 법칙으로 간주합니다.

엄지 손가락의 규칙

  • (matchStr, matchIndex)입력 문자열의 문자열 부분과 색인이 이미 섭취 된 것을 나타내는 연대순 으로 튜플 목록으로 일치를 고려하십시오 .
  • 이 목록은 정규식에 대한 입력 문자열의 왼쪽부터 지속적으로 작성됩니다.
  • 이미 먹은 부분은 더 이상 일치시킬 수 없습니다
  • 해당 위치에서 matchIndex부분 문자열 matchStr을 덮어 써서 주어진 인덱스에서 교체가 이루어집니다 . 인 경우 matchStr = ""“대체”는 사실상 삽입입니다.

공식적으로, 일치 및 교체 행위 는 다른 답변에서 볼 수 있듯이 루프로 설명됩니다 .

쉬운 예

  1. "abcd".replace(/.*/g, "x")출력 "xx":

    • 경기 목록은 [("abcd", 0), ("", 4)]

      특히 다음과 같은 이유로 생각할 수있는 다음 일치 항목은 포함 되지 않습니다 .

      • ("a", 0), ("ab", 0): 수량 *자는 탐욕 스럽다
      • ("b", 1), ("bc", 1): 이전 경기로 인해 ("abcd", 0)문자열 "b""bc"이미 먹었습니다
      • ("", 4), ("", 4) (즉, 두 번) : 인덱스 위치 4는 이미 첫 번째 명백한 일치로 먹었습니다.
    • 따라서 대체 문자열 "x"은 찾은 일치 문자열을 해당 위치에서 정확하게 대체합니다. 위치 0에서는 문자열을 대체하고 "abcd"위치 4에서는을 대체합니다 "".

      여기서 대체는 이전 문자열을 실제로 대체하거나 새 문자열을 삽입하는 역할을 할 수 있습니다.

  2. "abcd".replace(/.*?/g, "x")A의 지연 정량화 된*? 출력"xaxbxcxdx"

    • 경기 목록은 [("", 0), ("", 1), ("", 2), ("", 3), ("", 4)]

      앞의 예는 대조적으로, 여기에 ("a", 0), ("ab", 0), ("abc", 0), 또는 ("abcd", 0)때문에 엄밀하게는 최단 일치하는 항목을 찾습니다 제한하는 정량의 게으름에 포함되지 않습니다.

    • 모든 일치 문자열이 비어 있으므로 실제 대체는 발생하지 않지만 대신 x0, 1, 2, 3 및 4 위치에 삽입 됩니다.

  3. "abcd".replace(/.+?/g, "x")A의 지연 정량화 된+? 출력"xxxx"

    • 경기 목록은 [("a", 0), ("b", 1), ("c", 2), ("d", 3)]
  4. "abcd".replace(/.{2,}?/g, "x")A의 지연 정량화 된[2,}? 출력"xx"

    • 경기 목록은 [("ab", 0), ("cd", 2)]
  5. "abcd".replace(/.{0}/g, "x")"xaxbxcxdx"예제 2와 동일한 로직으로 출력 합니다.

더 어려운 예

항상 빈 문자열을 일치시키고 이러한 일치가 발생하는 위치를 제어하는 ​​경우 교체 대신 삽입 아이디어를 일관되게 활용할 수 있습니다 . 예를 들어, 모든 짝수 위치에서 빈 문자열과 일치하는 정규식을 만들어 거기에 문자를 삽입 할 수 있습니다.

  1. "abcdefgh".replace(/(?<=^(..)*)/g, "_"))A의 포지티브 lookbehind에(?<=...) 출력한다 "_ab_cd_ef_gh_"(단, 지금까지 크롬 지원)

    • 경기 목록은 [("", 0), ("", 2), ("", 4), ("", 6), ("", 8)]
  2. "abcdefgh".replace(/(?=(..)*$)/g, "_"))A의 긍정적 예측(?=...) 출력"_ab_cd_ef_gh_"

    • 경기 목록은 [("", 0), ("", 2), ("", 4), ("", 6), ("", 8)]

답변

첫 번째 일치는 분명히 "asdf"(위치 [0,4])입니다. 글로벌 플래그 ( g)가 설정 되었으므로 계속 검색합니다. 이 시점에서 (Position 4) 두 번째 일치하는 빈 문자열 (Position [4,4])을 찾습니다.

기억 *일치 이상의 요소를 제로.


답변