정규식에서 캡처하지 않은 그룹이란 무엇입니까?


답변:


2324

이것을 예로 들어 설명해 드리겠습니다.

다음 텍스트를 고려하십시오.

http://stackoverflow.com/
/programming/tagged/regex

이제 정규식을 아래에 적용하면 ...

(https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?

... 나는 다음과 같은 결과를 얻을 것입니다 :

Match "http://stackoverflow.com/"
     Group 1: "http"
     Group 2: "stackoverflow.com"
     Group 3: "/"

Match "/programming/tagged/regex"
     Group 1: "https"
     Group 2: "stackoverflow.com"
     Group 3: "/questions/tagged/regex"

그러나 나는 프로토콜에 신경 쓰지 않는다. 단지 URL의 호스트와 경로를 원한다. 따라서 정규 표현식을 변경하여 캡처하지 않는 그룹을 포함시킵니다 (?:).

(?:https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?

이제 내 결과는 다음과 같습니다.

Match "http://stackoverflow.com/"
     Group 1: "stackoverflow.com"
     Group 2: "/"

Match "/programming/tagged/regex"
     Group 1: "stackoverflow.com"
     Group 2: "/questions/tagged/regex"

보다? 첫 번째 그룹은 캡처되지 않았습니다. 파서는이 텍스트를 사용하여 텍스트와 일치하지만 나중에 최종 결과에서 무시합니다.


편집하다:

요청에 따라 그룹도 설명하려고합니다.

그룹은 여러 가지 목적으로 사용됩니다. 더 큰 일치 항목 (이름을 지정할 수도 있음)에서 정확한 정보를 추출하는 데 도움을주고 이전에 일치 한 그룹을 다시 일치시키고 대체에 사용할 수 있습니다. 몇 가지 예를 들어 봅시다.

어떤 종류의 XML 또는 HTML이 있다고 상상해보십시오 ( 정규식이 작업에 가장 적합한 도구는 아니지만 예를 들어 좋습니다). 태그를 파싱하고 싶기 때문에 다음과 같이 할 수 있습니다 (공백을 쉽게 이해할 수 있도록 추가했습니다).

   \<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
   \<(.+?)\> [^<]*? \</\1\>

첫 번째 정규식에는 이름이 지정된 그룹 (TAG)이 있고 두 번째 정규식에는 공통 그룹이 사용됩니다. 두 정규 표현식 모두 동일한 작업을 수행합니다. 첫 번째 그룹의 값 (태그 이름)을 사용하여 닫는 태그와 일치시킵니다. 차이점은 첫 번째는 이름을 사용하여 값과 일치하고 두 번째는 그룹 색인 (1에서 시작)을 사용한다는 것입니다.

이제 대체를 시도해 봅시다. 다음 텍스트를 고려하십시오.

Lorem ipsum dolor sit amet consectetuer feugiat fames malesuada pretium egestas.

이제이 멍청한 정규식을 사용하십시오.

\b(\S)(\S)(\S)(\S*)\b

이 정규식은 3 자 이상의 단어와 일치하며 그룹을 사용하여 처음 3자를 분리합니다. 결과는 다음과 같습니다.

Match "Lorem"
     Group 1: "L"
     Group 2: "o"
     Group 3: "r"
     Group 4: "em"
Match "ipsum"
     Group 1: "i"
     Group 2: "p"
     Group 3: "s"
     Group 4: "um"
...

Match "consectetuer"
     Group 1: "c"
     Group 2: "o"
     Group 3: "n"
     Group 4: "sectetuer"
...

따라서 대체 문자열을 적용하면 다음과 같습니다.

$1_$3$2_$4

... 위에서 첫 번째 그룹을 사용하고 밑줄을 추가하고 세 번째 그룹을 사용한 다음 두 번째 그룹을 사용하고 다른 밑줄을 추가 한 다음 네 번째 그룹을 사용하려고합니다. 결과 문자열은 다음과 같습니다.

L_ro_em i_sp_um d_lo_or s_ti_ a_em_t c_no_sectetuer f_ue_giat f_ma_es m_la_esuada p_er_tium e_eg_stas.

을 사용하여 명명 된 그룹을 대체 용으로 사용할 수도 있습니다 ${name}.

정규식 을 다루려면 http://regex101.com/을 권장합니다. http://regex101.com/ 은 정규식의 작동 방식에 대한 자세한 내용을 제공합니다. 또한 선택할 수있는 몇 가지 정규식 엔진을 제공합니다.


3
@ajsie : 결과에 대해 대체 작업을 수행하는 경우 기존 (캡처) 그룹이 가장 유용합니다. 다음은 쉼표로 구분 된 성 및 이름을 붙인 다음 순서를 바꾸는 (이름이 지정된 그룹 덕분에) 예입니다. regexhero.net/tester/?id=16892996-64d4-4f10-860a-24f28dad7e30
Steve Wortham

2
아니요, 동일하지 않습니다.
Ricardo Nolde

4
또한 캡처하지 않은 그룹은 정규식을 분할 구분 기호로 사용할 때 유일하게 유용합니다. "Alice and Bob"-split "\ s + (?: and | or) \ s +"
Yevgeniy

7
캡처하지 않는 그룹 (? :)과 lookahead 및 lookbehind 어설 션 (? =,?!)간에 차이가있는 것이 흥미로울 것입니다. 방금 정규 표현식에 대해 배우기 시작했지만 이해 한 바에 따르면 비 캡처 그룹은 일치하는 항목을 일치시키고 "반환"하는 데 사용되지만 "반환 값"은 역 참조를 위해 "저장"되지 않습니다. 반면에 Lookahead와 lookbehind 어설 션은 "저장"될뿐만 아니라 일치의 일부도 아니며 단지 무언가가 일치한다고 주장하지만 실수하지 않으면 "일치"값이 무시됩니다.
Christian

5
[] 세트입니다. [123]은 세트 안의 모든 문자를 한 번 일치시킵니다. [^ 123]은 (는) 세트 내부에없는 항목과 한 번 일치합니다. [^ / \ r \ n] +는 /, \ r, \ n과 다른 하나 이상의 문자와 일치합니다.
Ricardo Nolde

180

캡처 그룹을 사용하여 식을 구성하고 구문 분석 할 수 있습니다. 캡처하지 않은 그룹은 첫 번째 이점이 있지만 두 번째 그룹의 오버 헤드는 없습니다. 예를 들어 캡처하지 않은 그룹은 선택 사항이라고 말할 수 있습니다.

숫자 텍스트를 일치 시키려고하지만 일부 숫자는 1, 2, 3, 4로 기록 될 수 있습니다. 숫자 부분을 캡처하려면 (선택적) 접미사는 제외하고 캡처하지 않는 그룹을 사용할 수 있습니다 .

([0-9]+)(?:st|nd|rd|th)?

1, 2, 3 ... 또는 1, 2, 3, ... 형식의 숫자와 일치하지만 숫자 부분 만 캡처합니다.


3
간결하고 아마도 여기에 가장 좋은 설명입니다.
NelsonGon

106

?: 식을 그룹화 할 때 사용되지만 문자열의 일치 / 캡처 된 부분으로 저장하지는 않습니다.

예를 들어 IP 주소와 일치하는 것이 있습니다.

/(?:\d{1,3}\.){3}\d{1,3}/

처음 3 옥텟을 저장하는 것에 대해서는 신경 쓰지 않지만 (?:...)그룹화를 사용하면 일치하는 캡처 및 저장 오버 헤드가 발생하지 않고 정규 표현식을 단축 할 수 있습니다.


38

그룹을 캡처하지 못하게하므로 해당 그룹과 일치하는 하위 문자열이 캡처 목록에 포함되지 않습니다. 차이점을 설명하기위한 루비의 예 :

"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]

여기서 "abc".match (/.(.)./). captures를 사용할 수없는 이유는 무엇입니까?
PRASANNA SARAF 9

@PRASANNASARAF 물론 가능합니다. 코드의 요점은 (?:)유용한 예제를 보여주지 않고 캡처를 생성하지 않음 을 보여주는 것이 었습니다 (?:). (?:)하위 표현식을 그룹화하려는 경우 (예 : 비 원자 하위 표현식에 수량자를 적용하려는 경우 또는의 범위를 제한하려는 경우) 유용 |하지만 아무 것도 캡처하지 않으려 는 경우에 유용합니다 .
sepp2k

26

역사적 동기 :

비 캡처 그룹의 존재는 괄호를 사용하여 설명 할 수 있습니다.

식을 고려 (a|b)c하고 a|bc인해 연결을 통해 우선 순위로, |이 표현은 두 개의 서로 다른 언어 (대표 {ac, bc}하고 {a, bc}각각을).

그러나 괄호는 일치하는 그룹으로도 사용됩니다 (다른 답변에서 설명한 것처럼 ...).

괄호를 갖고 싶지만 하위 표현식을 캡처하지 않으려면 비 캡처 그룹을 사용하십시오. 이 예에서는(?:a|b)c


6
왜 그런지 궁금했습니다. "이유"가이 정보를 암기하는 데 중요하다고 생각합니다.
JMI MADISON

22

예를 들어 이것을 시도해 보겠습니다.

정규식 코드 : (?:animal)(?:=)(\w+)(,)\1\2

검색 문자열 :

1 호선- animal=cat,dog,cat,tiger,dog

2 호선- animal=cat,cat,dog,dog,tiger

3 호선- animal=dog,dog,cat,cat,tiger

(?:animal) -> 캡처되지 않은 그룹 1

(?:=)-> 캡처되지 않은 그룹 2

(\w+)-> 캡처 그룹 1

(,)-> 캡처 그룹 2

\1 -> 캡처 된 그룹 1의 결과, 즉 1 행은 고양이, 2 행은 고양이, 3 행은 개입니다.

\2 -> 캡처 된 그룹 2의 결과, 즉 쉼표 (,)

따라서이 코드에서는 나중에 코드에서 캡처 된 그룹 1과 2의 결과를 제공 \1하고 \2반복합니다.

코드 순서에 (?:animal)따라 그룹 1 (?:=)이어야 하고 그룹 2 여야하며 계속되어야합니다.

그러나 ?:일치 그룹을 캡처하지 않으면 (일치 그룹에서 계산되지 않으므로 그룹 번호는 첫 번째 캡처 그룹에서 시작하고 캡처되지 않은 그룹에서 시작) 일치 그룹 결과의 반복 (?:animal)나중에 코드에서 호출 할 수 없습니다.

이것이 비 캡처 그룹의 사용을 설명하기를 바랍니다.

여기에 이미지 설명을 입력하십시오


14

캡처 한 그룹은 나중에 정규식에서 사용하여 일치 시키 거나 정규식 의 대체 부분에서 사용할 수 있습니다. 캡처하지 않는 그룹을 만들면 해당 그룹이 이러한 이유로 사용되지 않습니다.

캡처하지 않는 그룹은 여러 가지 다른 것을 캡처하려고하고 캡처하지 않으려는 그룹이있는 경우 유용합니다.

그것이 그들이 존재하는 이유와 거의 같습니다. 그룹에 대해 배우고 원자 그룹에 대해 배우 면 많은 일을합니다! 둘러보기 그룹도 있지만 조금 더 복잡하고 많이 사용되지는 않습니다.

정규식에서 나중에 사용하는 예 (역 참조) :

<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1> [ss 지원없이 xml 태그를 찾습니다]]

([A-Z][A-Z0-9]*) 캡처 그룹 (이 경우 태그 이름)

나중에 정규 표현식 \1에서 첫 번째 그룹 ( ([A-Z][A-Z0-9]*)그룹) 과 동일한 텍스트 만 일치합니다 (이 경우 종료 태그와 일치 함).


나중에 OR을 맞추기 위해 어떻게 사용되는지에 대한 간단한 예를 들어 줄 수 있습니까?
never_had_a_name

나는 당신이 나중에 일치하는 데 사용할 수 있거나 교체에 사용할 수 있음을 의미합니다. 또는 그 문장에서 캡처 그룹에 대한 두 가지 용도가 있음을 보여주기위한 것입니다
Bob Fincheimer

9

글쎄, 나는 JavaScript 개발자이며 JavaScript와 관련된 그 중요성을 설명하려고 노력할 것이다.

cat is animal 고양이와 동물 을 일치시키고 싶을 때 일치시키려는 시나리오를 고려하십시오 is.

 // this will ignore "is" as that's is what we want
"cat is animal".match(/(cat)(?: is )(animal)/) ;
result ["cat is animal", "cat", "animal"]

 // using lookahead pattern it will match only "cat" we can
 // use lookahead but the problem is we can not give anything
 // at the back of lookahead pattern
"cat is animal".match(/cat(?= is animal)/) ;
result ["cat"]

 //so I gave another grouping parenthesis for animal
 // in lookahead pattern to match animal as well
"cat is animal".match(/(cat)(?= is (animal))/) ;
result ["cat", "cat", "animal"]

 // we got extra cat in above example so removing another grouping
"cat is animal".match(/cat(?= is (animal))/) ;
result ["cat", "animal"]

7

복잡한 정규 표현식에서는 반복 일치를 위해 존재하는 그룹과 다시 참조를 제공하는 그룹을 사용하는 상황이 발생할 수 있습니다. 기본적으로 각 그룹과 일치하는 텍스트가 역 참조 배열에로드됩니다. 그룹이 많고 역 참조 배열에서 그룹 중 일부만 참조 할 수 있어야하는 경우이 기본 동작을 재정 의하여 특정 그룹이 반복 처리를 위해서만 있고 캡처 및 저장하지 않아도된다는 정규식을 알릴 수 있습니다. 역 참조 배열에서.


7

나는 이것을 말하기 위해 최고 답변에 대해 언급 할 수 없다 : 나는 최고 답변에만 암시되는 명시 적 요점을 추가하고 싶다 :

캡처 (?...) 하지 않은 그룹 은 원래의 완전 일치 항목에서 문자를 제거 하지 않으며 프로그래머에게 시각적으로 정규식을 재구성합니다.

외부 문자를 정의하지 않고 정규식의 특정 부분에 액세스하려면 항상 사용해야합니다. .group(<index>)


2
나머지 답변에서 빠진 가장 중요한 힌트를 제공했습니다. 원하는 결과를 얻지 못했기 때문에 모든 예제를 시도하고 가장 많은 expletives를 사용했습니다. 귀하의 게시물 만 내가 잘못 된 곳을 보여주었습니다.
Seshadri R

그것을 듣고 기뻐!
Scott Anderson

6

이름에서 알 수 있듯이 tl; dr 비 캡처 그룹은 일치에 포함하지 않으려는 정규 표현식의 일부이며 ?:그룹을 비 캡처로 정의하는 방법입니다.

이메일 주소가 있다고 가정 해 봅시다 example@example.com. 다음 정규식은 id 부분과 @ example.com 부분의 두 그룹 을 만듭니다 . (\p{Alpha}*[a-z])(@example.com). 간단히하기 위해 @캐릭터를 포함한 전체 도메인 이름을 추출하고 있습니다.

이제 주소의 ID 부분 만 있으면됩니다. 당신이하고 싶은 ()것은 정규식으로 둘러싸인 일치하는 결과의 첫 번째 그룹을 얻는 것입니다.이를 수행하는 방법은 캡처하지 않는 그룹 구문을 사용하는 것 ?:입니다. 따라서 정규 표현식 (\p{Alpha}*[a-z])(?:@example.com)은 이메일의 id 부분 만 반환합니다.


5

내가 만난 흥미로운 점 중 하나는 캡처하지 않은 그룹 내에 캡처 그룹을 가질 수 있다는 것입니다. 일치하는 웹 URL에 대한 정규식 아래를 살펴보십시오.

var parse_url_regex = /^(?:([A-Za-z]+):)(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;

입력 URL 문자열 :

var url = "http://www.ora.com:80/goodparts?q#fragment";

내 정규 표현식의 첫 번째 그룹은 (?:([A-Za-z]+):)프로토콜 방식과 대장과 일치하는 비 캡처 그룹 :문자 즉 http:하지만 코드 아래 실행되었을 때, 나는 문자열이 포함 된 반환 된 배열의 첫 번째 인덱스를보고 있었다 http내가 생각했을 때 http대장 :캡처되지 않은 그룹 내에 있으므로 둘 다보고되지 않습니다.

console.debug(parse_url_regex.exec(url));

여기에 이미지 설명을 입력하십시오

첫 번째 그룹 (?:([A-Za-z]+):)이 캡처하지 않는 그룹이라면 http출력 배열에서 문자열을 반환하는 이유를 생각했습니다 .

따라서 ([A-Za-z]+)캡처하지 않은 그룹 내에 중첩 된 그룹이있는 것을 알 수 있습니다 . 해당 중첩 그룹 ([A-Za-z]+)은 캡처 그룹 ?:이 아닌 그룹 자체 (처음에는 포함 하지 않음 )입니다 (?:([A-Za-z]+):). 텍스트가 http여전히 캡처되지만 :캡처되지 않은 그룹 내부에 있지만 캡처 그룹 외부에있는 콜론 문자가 출력 배열에보고되지 않는 이유입니다.


2

Chrome devTools를 연 다음 콘솔 탭을 열고 다음을 입력하십시오.

"Peace".match(/(\w)(\w)(\w)/)

실행하면 다음이 표시됩니다.

["Pea", "P", "e", "a", index: 0, input: "Peace", groups: undefined]

JavaScript정규식 엔진 캡처 세 그룹, 인덱스 1,2,3와 항목. 이제 캡처하지 않는 마크를 사용하여 결과를 봅니다.

"Peace".match(/(?:\w)(\w)(\w)/)

결과는 다음과 같습니다.

["Pea", "e", "a", index: 0, input: "Peace", groups: undefined]

이것은 캡처하지 않은 그룹이 무엇인지 분명합니다.


2

나는 당신에게 답을 줄 것이라고 생각합니다. 일치 여부를 확인하지 않고 캡처 변수를 사용하지 마십시오.

$1일치가 성공하지 않으면 캡처 변수 등이 유효하지 않으며 지워지지 않습니다.

#!/usr/bin/perl  
use warnings;
use strict;   
$_ = "bronto saurus burger";
if (/(?:bronto)? saurus (steak|burger)/)
{
    print "Fred wants a  $1";
}
else
{
    print "Fred dont wants a $1 $2";
}

위의 예에서의 캡처 브론토 피하기 위해 $1, (?:)사용된다.

패턴이 일치 $1하면 다음 그룹화 된 패턴으로 캡처됩니다.

따라서 출력은 다음과 같습니다.

Fred wants a burger

일치하는 내용을 저장하지 않으려는 경우에 유용합니다.


1

매우 간단합니다. 간단한 날짜 예제로 이해할 수 있습니다. 날짜가 2019 년 1 월 1 일 또는 2019 년 5 월 2 일 또는 다른 날짜로 언급되고 날짜를 dd / mm / yyyy 형식 으로 변환하려고한다고 가정합니다. 해당 문제에 대해 1 월 또는 2 월의 이름을 지정하므로 숫자 부분을 캡처하지만 (선택적) 접미사를 캡처하지 않으려면 캡처하지 않은 그룹을 사용할 수 있습니다.

정규식은

([0-9]+)(?:January|February)?

그렇게 간단합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.