캡처하지 않은 그룹, 즉 (?:)
정규 표현식에 어떻게 사용되며 어떤 이점이 있습니까?
캡처하지 않은 그룹, 즉 (?:)
정규 표현식에 어떻게 사용되며 어떤 이점이 있습니까?
답변:
이것을 예로 들어 설명해 드리겠습니다.
다음 텍스트를 고려하십시오.
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/ 은 정규식의 작동 방식에 대한 자세한 내용을 제공합니다. 또한 선택할 수있는 몇 가지 정규식 엔진을 제공합니다.
캡처 그룹을 사용하여 식을 구성하고 구문 분석 할 수 있습니다. 캡처하지 않은 그룹은 첫 번째 이점이 있지만 두 번째 그룹의 오버 헤드는 없습니다. 예를 들어 캡처하지 않은 그룹은 선택 사항이라고 말할 수 있습니다.
숫자 텍스트를 일치 시키려고하지만 일부 숫자는 1, 2, 3, 4로 기록 될 수 있습니다. 숫자 부분을 캡처하려면 (선택적) 접미사는 제외하고 캡처하지 않는 그룹을 사용할 수 있습니다 .
([0-9]+)(?:st|nd|rd|th)?
1, 2, 3 ... 또는 1, 2, 3, ... 형식의 숫자와 일치하지만 숫자 부분 만 캡처합니다.
그룹을 캡처하지 못하게하므로 해당 그룹과 일치하는 하위 문자열이 캡처 목록에 포함되지 않습니다. 차이점을 설명하기위한 루비의 예 :
"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]
(?:)
유용한 예제를 보여주지 않고 캡처를 생성하지 않음 을 보여주는 것이 었습니다 (?:)
. (?:)
하위 표현식을 그룹화하려는 경우 (예 : 비 원자 하위 표현식에 수량자를 적용하려는 경우 또는의 범위를 제한하려는 경우) 유용 |
하지만 아무 것도 캡처하지 않으려 는 경우에 유용합니다 .
역사적 동기 :
비 캡처 그룹의 존재는 괄호를 사용하여 설명 할 수 있습니다.
식을 고려 (a|b)c
하고 a|bc
인해 연결을 통해 우선 순위로, |
이 표현은 두 개의 서로 다른 언어 (대표 {ac, bc}
하고 {a, bc}
각각을).
그러나 괄호는 일치하는 그룹으로도 사용됩니다 (다른 답변에서 설명한 것처럼 ...).
괄호를 갖고 싶지만 하위 표현식을 캡처하지 않으려면 비 캡처 그룹을 사용하십시오. 이 예에서는(?:a|b)c
예를 들어 이것을 시도해 보겠습니다.
정규식 코드 : (?: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)
나중에 코드에서 호출 할 수 없습니다.
이것이 비 캡처 그룹의 사용을 설명하기를 바랍니다.
캡처 한 그룹은 나중에 정규식에서 사용하여 일치 시키 거나 정규식 의 대체 부분에서 사용할 수 있습니다. 캡처하지 않는 그룹을 만들면 해당 그룹이 이러한 이유로 사용되지 않습니다.
캡처하지 않는 그룹은 여러 가지 다른 것을 캡처하려고하고 캡처하지 않으려는 그룹이있는 경우 유용합니다.
그것이 그들이 존재하는 이유와 거의 같습니다. 그룹에 대해 배우고 원자 그룹에 대해 배우 면 많은 일을합니다! 둘러보기 그룹도 있지만 조금 더 복잡하고 많이 사용되지는 않습니다.
정규식에서 나중에 사용하는 예 (역 참조) :
<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1>
[ss 지원없이 xml 태그를 찾습니다]]
([A-Z][A-Z0-9]*)
캡처 그룹 (이 경우 태그 이름)
나중에 정규 표현식 \1
에서 첫 번째 그룹 ( ([A-Z][A-Z0-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"]
나는 이것을 말하기 위해 최고 답변에 대해 언급 할 수 없다 : 나는 최고 답변에만 암시되는 명시 적 요점을 추가하고 싶다 :
캡처 (?...)
하지 않은 그룹 은 원래의 완전 일치 항목에서 문자를 제거 하지 않으며 프로그래머에게 시각적으로 만 정규식을 재구성합니다.
외부 문자를 정의하지 않고 정규식의 특정 부분에 액세스하려면 항상 사용해야합니다. .group(<index>)
이름에서 알 수 있듯이 tl; dr 비 캡처 그룹은 일치에 포함하지 않으려는 정규 표현식의 일부이며 ?:
그룹을 비 캡처로 정의하는 방법입니다.
이메일 주소가 있다고 가정 해 봅시다 example@example.com
. 다음 정규식은 id 부분과 @ example.com 부분의 두 그룹 을 만듭니다 . (\p{Alpha}*[a-z])(@example.com)
. 간단히하기 위해 @
캐릭터를 포함한 전체 도메인 이름을 추출하고 있습니다.
이제 주소의 ID 부분 만 있으면됩니다. 당신이하고 싶은 ()
것은 정규식으로 둘러싸인 일치하는 결과의 첫 번째 그룹을 얻는 것입니다.이를 수행하는 방법은 캡처하지 않는 그룹 구문을 사용하는 것 ?:
입니다. 따라서 정규 표현식 (\p{Alpha}*[a-z])(?:@example.com)
은 이메일의 id 부분 만 반환합니다.
내가 만난 흥미로운 점 중 하나는 캡처하지 않은 그룹 내에 캡처 그룹을 가질 수 있다는 것입니다. 일치하는 웹 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
여전히 캡처되지만 :
캡처되지 않은 그룹 내부에 있지만 캡처 그룹 외부에있는 콜론 문자가 출력 배열에보고되지 않는 이유입니다.
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]
이것은 캡처하지 않은 그룹이 무엇인지 분명합니다.
나는 당신에게 답을 줄 것이라고 생각합니다. 일치 여부를 확인하지 않고 캡처 변수를 사용하지 마십시오.
$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
일치하는 내용을 저장하지 않으려는 경우에 유용합니다.