RegExp의 exec () 함수와 String의 match () 함수의 차이점은 무엇입니까?


122

이것을 실행하면 :

/([^\/]+)+/g.exec('/a/b/c/d');

나는 이것을 얻는다 :

["a", "a"]

그러나 이것을 실행하면 :

'/a/b/c/d'.match(/([^\/]+)+/g);

그런 다음 예상되는 결과를 얻습니다.

["a", "b", "c", "d"]

차이점이 뭐야?


4
exec모든 하위 선택을 얻기 위해 반복 합니다.
zzzzBov

2
두 번째 +match이미 모든 하위 표현식을 반환 하므로 필요하지 않습니다 . .exec매번 하나만 반환하므로 필요하지 않습니다 +.
pimvdb

3
또한 두 개의 플러스와 같은 중첩 된 수량자는 재앙적인 역 추적으로 쉽게 이어지기 때문에 매우 신중하게 사용해야합니다 .
Marius Schulz

1
@MariusSchulz 링크 주셔서 감사합니다. 그것은 내가 소유 적 수량 자와 원자 그룹화에 대해 배우도록 이끌었습니다. 이해하기 아주 좋은 것.
Justin Warkentin 2013 년

답변:


117

exec전역 정규식을 사용하는 것은 여전히 ​​일치하는 모든 하위 표현식을 검색하므로 루프에서 사용됩니다. 그래서:

var re = /[^\/]+/g;
var match;

while (match = re.exec('/a/b/c/d')) {
    // match is now the next match, in array form.
}

// No more matches.

String.match 이 작업을 수행하고 캡처 된 그룹을 버립니다.


39
이 답변에 추가 할 내용이 있습니다. while 조건 내에 정규식 리터럴을 배치하지 않아야합니다. 그렇지 않으면 while(match = /[^\/]+/g.exec('/a/b/c/d')무한 루프가 생성됩니다!. 그것은 명확하게 MDN에 명시된 것으로 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
yeyo

7
@yeyo : 좀 더 구체적으로 말하자면, 동일한 정규식 객체 여야합니다. 리터럴은 그것을 달성하지 못합니다.
Ry-

@ Ry- ES5에서이 동작이 도입되었음을 주목해야한다고 생각합니다. ES5 전에 new RegExp("pattern")/pattern/의미가 다른 것.
Robert

75

한 장의 사진이 더 좋습니다.

re_once = /([a-z])([A-Z])/
re_glob = /([a-z])([A-Z])/g

st = "aAbBcC"

console.log("match once="+ st.match(re_once)+ "  match glob="+ st.match(re_glob))
console.log("exec once="+ re_once.exec(st) + "   exec glob="+ re_glob.exec(st))
console.log("exec once="+ re_once.exec(st) + "   exec glob="+ re_glob.exec(st))
console.log("exec once="+ re_once.exec(st) + "   exec glob="+ re_glob.exec(st))

차이점이 보이십니까?

참고 : 강조 표시하려면 일치 된 패턴 (예 : aA) 후에 캡처 된 그룹 (예 : a, A)이 반환되며 일치하는 패턴이 아닙니다.


28

/regex/.exec()찾은 첫 번째 일치 항목 만 "string".match()반환 g하고 정규 표현식 에서 플래그 를 사용하면 모두 반환합니다 .

여기를보십시오 : exec , match .


23

정규식이 전역이고 캡처하는 경우 exec를 사용해야합니다. Match는 모든 캡처를 반환하지 않습니다.

일치는 일치 (캡처하지 않음) 할 때 효과적입니다. 한 번 실행하면 모든 일치 항목이 배열됩니다. (정규식이 전역이 아닌 경우 일치는 일치를 표시하고 캡처가 이어짐)

Exec은 캡처 할 때 사용하는 것이며 실행될 때마다 일치를 제공하고 캡처가 이어집니다. (일치는 정규식이 전역이 아닌 경우에만 전체 일치 후 캡처를 제공하는 방식으로 작동합니다).

Exec의 또 다른 용도는 일치 항목의 인덱스 또는 위치를 가져 오는 것입니다. 정규식에 대한 변수가 있으면 .lastIndex를 사용하여 일치하는 위치를 가져올 수 있습니다. regex 객체에는 .lastIndex가 있고 regex 객체는 .exec를 수행하는 작업입니다. 점 일치는 문자열에서 수행되며 정규식 개체 dot lastIndex를 수행 할 수 없습니다.

문자열에는 정규식이 전달되는 일치 함수가 있습니다. 정규식에는 exec 함수가 있으며 문자열이 전달됩니다.

exec 당신은 여러 번 실행합니다. 한 번 뛰는 경기

캡처하지 않을 때 match를 사용하는 것이 좋으며 캡처 할 때 캡처를 얻는 데 유용하기 때문에 더 강력한 exec를 사용할 수 있지만 캡처 할 때 match를 사용했다면 정규식이 전역이 아닌 경우 캡처를 표시하는 것을 확인하십시오. 정규식이 전역일 때 캡처를 표시하지 않습니다.

> "azb".match(/a(z)b/);
[ "azb", "z" ]

> "azb".match(/a(z)b/g);
[ "azb" ]
>

또 다른 것은 exec를 사용하는 경우 정규식에서 호출된다는 점에 유의하고 정규식에 대한 변수를 사용하면 더 많은 권한을 갖게됩니다.

정규식에 변수를 사용하지 않으면 일치 항목을 얻지 못하므로 exec를 사용할 때 정규식에 대한 변수를 사용하십시오.

> /./g.exec("abc")
[ "a" ]
> /./g.exec("abc")
[ "a" ]
> /./g.exec("abc")
[ "a" ]
>
> /[a-c]/g.exec("abc")
[ "a" ]
> /[a-c]/g.exec("abc")
[ "a" ]
>

> var r=/[a-c]/g
> r.exec("abc")
[ "a" ]
> r.exec("abc")
[ "b" ]
> r.exec("abc")
[ "c" ]
> r.exec("abc")
null
>

그리고 exec를 사용하면 일치의 "인덱스"를 얻을 수 있습니다.

> var r=/T/g
> r.exec("qTqqqTqqTq");
[ "T" ]
> r.lastIndex
2
> r.exec("qTqqqTqqTq");
[ "T" ]
> r.lastIndex
6
> r.exec("qTqqqTqqTq");
[ "T" ]
> r.lastIndex
9
> r.exec("qTqqqTqqTq");
null
> r.lastIndex
0
>

따라서 인덱스 또는 캡처를 원하면 exec를 사용하십시오 ( "인덱스"와 함께 제공되는 "인덱스"는 실제로 n 번째 발생이며 1에서 계산됩니다. 따라서 적절한 값을 유도 할 수 있습니다. 1을 빼서 색인을 생성합니다. 그리고 보시다시피 0-lastIndex 0-찾을 수 없음)을 제공합니다.

일치를 늘리려면 캡처 할 때 사용할 수 있지만 정규식이 전역일 때는 사용할 수 없으며이를 위해 수행 할 때 배열의 내용이 모두 일치하는 것은 아니지만 전체 캡처 후 일치합니다.


예,의 작업을 이해하는 것은 r.lastIndex차이 이해하는 핵심 요소 execmatch.
runsun

@barlop "일치가 모든 캡처와 일치하지는 않습니다", 진지하게? "a, b, c, aa, bb, cc".match (/ (\ w +) / g) => [ "a", "b", "c", "aa", "bb", "cc" ]. 그것들을 모두 캐시했다고 설명하는 방법은 무엇입니까?
MrHIDEn

@barlop If your regex is global, and you are capturing, then you must use exec. Match won't return all your captures.콘솔에서 얻었습니다. "a,b,c,aa,bb,cc".match(/(\w+)/g);Opera, Firefox를 복사 / 붙여 넣기 만하면 됩니다.
MrHIDEn

@MrHIDEn 나는 당신이 잘못된 인용구에서 한 언어를 사용하지 않을 것입니다. 그리고 중요한 것은 무엇이 보여지고 우리가 무엇을 볼 수 있는지입니다.이면에 캐싱이 있는지 여부도 관련이 없습니다. 그리고 제가 이것을 조사한 지 오래되었지만 모든 캡처를 보여주지는 않습니다. 당신의 예 "a,b,c,aa,bb,cc".match(/(\w+)/g) 를 들어도 무슨 일이 일어나고 있는지는 모든 경기를 보여줍니다. 모든 캡처를 표시하는 경우 정확히 동일하게 표시됩니다 (cntd)
barlop

(cntd) 그래서 아마도 당신은 그것이 캡처를 보여주고 있다고 생각할 수 있지만 그렇지 않습니다. 그것은 경기를 보여주고 있습니다
barlop 19

6

.match () 함수는 str.match(regexp)다음 작업을 수행합니다 :

  • 이 경우 입니다 일치가 반환합니다 :
    • g플래그 정규 표현식에서 사용되는 경우 : 모든 하위 문자열을 반환합니다 (캡처 그룹 무시).
    • 경우 g플래그가되어 있지 정규 표현식에 사용 : 그것은 같은를 반환합니다regexp.exec(str)
  • 일치하는 항목 이 없으면 다음을 반환합니다.
    • null

플래그를 사용하는 .match ()의g:

var str = "qqqABApppabacccaba";
var e1, e2, e3, e4, e5;
e1 = str.match(/nop/g); //null
e2 = str.match(/no(p)/g); //null
e3 = str.match(/aba/g); //["aba", "aba"]
e4 = str.match(/aba/gi); //["ABA", "aba", "aba"]
e5 = str.match(/(ab)a/g); //["aba", "aba"] ignoring capture groups as it is using the g flag

그리고 플래그가 없는 .match ()g.exec ()와 같습니다.

e1=JSON.stringify(str.match(/nop/))===JSON.stringify(/nop/.exec(str)); //true
//e2 ... e4 //true
e5=JSON.stringify(str.match(/(ab)a/))===JSON.stringify(/(ab)a/.exec(str)); //true

.exec () 함수는 regexp.exec(str)다음 작업을 수행합니다 :

  • 이 경우 입니다 일치가 반환합니다 :
    • g플래그 정규 표현식에서 사용되는 경우 : 다음 일치 항목을 ( 호출 될 때마다) 반환 합니다. 중요 : regexp 객체가 변수에 저장되지 않은 경우 다음 일치 항목으로 진행되지 않습니다 (동일한 객체 여야 함).[N_MatchedStr, N_Captured1, N_Captured2, ...]N
    • g플래그가 정규 표현식에서 사용 되지 않는 경우 : 플래그가 g있고 처음으로 한 번만 호출 된 것처럼 동일한 결과를 반환합니다 .
  • 일치하는 항목 이 없으면 다음을 반환합니다.
    • null

.exec ()의 예 (저장된 regexp + g플래그 사용 = 각 호출에 따라 변경됨) :

var str = "qqqABApppabacccaba";
var myexec, rgxp = /(ab)a/gi;

myexec = rgxp.exec(str);
console.log(myexec); //["ABA", "AB"]
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "ab"]
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "ab"]
myexec = rgxp.exec(str);
console.log(myexec); //null

//But in this case you should use a loop:
var mtch, myRe = /(ab)a/gi;
while(mtch = myRe.exec(str)){ //infinite looping with direct regexps: /(ab)a/gi.exec()
    console.log("elm: "+mtch[0]+" all: "+mtch+" indx: "+myRe.lastIndex);
    //1st iteration = elm: "ABA" all: ["ABA", "AB"] indx: 6
    //2nd iteration = elm: "aba" all: ["aba", "ab"] indx: 12
    //3rd iteration = elm: "aba" all: ["aba", "ab"] indx: 18
}

.exec () 가 호출 할 때 마다 변경 되지 않는 경우의 예 :

var str = "qqqABApppabacccaba", myexec, myexec2;

//doesn't go into the next one because no g flag
var rgxp = /(a)(ba)/;
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "a", "ba"]
myexec = rgxp.exec(str);
console.log(myexec); //["aba", "a", "ba"]
//... ["aba", "a", "ba"]

//doesn't go into the next one because direct regexp
myexec2 = /(ab)a/gi.exec(str);
console.log(myexec2); //["ABA", "AB"]
myexec2 = /(ab)a/gi.exec(str);
console.log(myexec2); //["ABA", "AB"]
//... ["ABA", "AB"]

1

때때로 regex.exec ()는 string.match () 보다 훨씬 더 많은 시간 이 걸립니다 .

string.match ()와 regex.exec ()의 결과가 같으면 (예 : \ g 플래그를 사용하지 않을 때) regex.exec ()는 x2에서 x30 사이의 어딘가를 취한 다음 string을 취합니다. 시합():

따라서 이러한 경우 "new RegExp (). exec ()"접근 방식을 사용하는 것은 전역 정규식이 필요할 때만 사용해야합니다 (예 : 두 번 이상 실행).


1
벤치 마크가 있습니까?
Sơn Trần-Nguyễn
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.