한스, 나는 미끼를 가져다가 내 이전 대답을 구체화하겠습니다. "좀 더 완전한 것"을 원한다고 하셨기 때문에 긴 대답이 마음에 들지 않으시 길 바랍니다. 몇 가지 배경 지식부터 시작하겠습니다.
우선, 이것은 훌륭한 질문입니다. 특정 컨텍스트 (예 : 코드 블록 또는 괄호 내부)를 제외하고 특정 패턴 일치에 대한 질문이 자주 있습니다. 이러한 질문은 종종 상당히 어색한 해결책을 제시합니다. 따라서 여러 상황 에 대한 귀하의 질문은 특별한 도전입니다.
놀라다
놀랍게도 일반적이고 구현하기 쉬우 며 유지 관리하기 좋은 효율적인 솔루션이 하나 이상 있습니다. 그것은 모든 정규식 맛을 작동 코드에서 캡처 그룹을 검사 할 수 있습니다. 그리고 처음에는 당신과 다르게 들릴 수있는 몇 가지 일반적인 질문에 대답합니다. "도너츠를 제외한 모든 항목 일치", "모두 교체 ...", "엄마의 블랙리스트에있는 단어를 제외한 모든 단어 일치", "무시" 태그 ","기울임 꼴이 아닌 경우 온도 일치 "...
슬프게도이 기술은 잘 알려져 있지 않습니다. 나는 그것을 사용할 수있는 20 개의 SO 질문 중 하나만이 그것을 언급하는 하나의 답을 가지고 있다고 추정합니다. 이는 아마도 50 개 또는 60 개의 답 중 하나를 의미합니다. 코멘트에서 Kobi와의 교환을 참조하십시오. 이 기술은 (낙관적으로) "역대 최고의 정규식 트릭"이라고 부르는 이 기사 에서 자세히 설명 합니다. 자세히 설명하지 않고 기술이 어떻게 작동하는지 확실히 이해하려고 노력할 것입니다. 다양한 언어로 된 자세한 내용과 코드 샘플을 보려면 해당 리소스를 참조하십시오.
더 잘 알려진 변형
동일한 작업을 수행하는 Perl 및 PHP에 특정한 구문을 사용하는 변형이 있습니다. CasimiretHippolyte 및 HamZa 와 같은 정규식 마스터의 손에서 SO에서 볼 수 있습니다. 아래에서 이에 대해 자세히 설명하지만 여기서는 모든 정규식 버전에서 작동하는 일반 솔루션에 중점을 둡니다 (코드에서 캡처 그룹을 검사 할 수있는 한).
모든 배경에 감사드립니다, zx81 ...하지만 레시피는 무엇입니까?
주요 사실
이 메서드는 그룹 1 캡처에서 일치를 반환합니다. 전체 경기에 대해서는 전혀 신경 쓰지 않습니다.
사실, 트릭은 우리가 원하지 않는 다양한 컨텍스트 ( |
OR / 교대를 사용하여 이러한 컨텍스트를 연결 )를 일치시켜 "중화"하는 것입니다. 원치 않는 모든 컨텍스트를 일치시킨 후 교체의 마지막 부분은 우리 가 원하는 것과 일치 하여 그룹 1에 캡처합니다.
일반적인 레시피는
Not_this_context|Not_this_either|StayAway|(WhatYouWant)
이것은 일치 할 Not_this_context
것이지만, 어떤 의미에서 일치는 쓰레기통에 들어갑니다. 왜냐하면 우리는 전체 경기를 보지 않을 것이기 때문입니다. 우리는 그룹 1 캡처 만 봅니다.
귀하의 경우, 귀하의 숫자와 세 가지 컨텍스트를 무시하면 다음을 수행 할 수 있습니다.
s1|s2|s3|(\b\d+\b)
s1, s2 및 s3를 둘러보기로 피하는 대신 실제로 일치시키기 때문에 s1, s2 및 s3에 대한 개별 표현식은 하루 동안 명확하게 유지 될 수 있습니다. (그들은 a의 각 측면에있는 하위 표현식입니다 |
)
전체 표현식은 다음과 같이 작성할 수 있습니다.
(?m)^.*\.$|\([^\)]*\)|if\(.*?//endif|(\b\d+\b)
이 데모를 참조하십시오 (그러나 오른쪽 아래 창의 캡처 그룹에 중점을 둡니다.)
정신적으로 각 |
구분 기호 에서이 정규식을 분할하려고하면 실제로는 일련의 매우 간단한 식입니다.
여유 공간을 지원하는 플레이버의 경우 특히 잘 읽습니다.
(?mx)
### s1: Match line that ends with a period ###
^.*\.$
| ### OR s2: Match anything between parentheses ###
\([^\)]*\)
| ### OR s3: Match any if(...//endif block ###
if\(.*?//endif
| ### OR capture digits to Group 1 ###
(\b\d+\b)
이것은 매우 읽고 유지하기 쉽습니다.
정규식 확장
더 많은 상황 s4 및 s5를 무시하려면 왼쪽에 더 많은 상황을 추가합니다.
s4|s5|s1|s2|s3|(\b\d+\b)
어떻게 작동합니까?
원하지 않는 컨텍스트는 왼쪽의 대체 목록에 추가됩니다. 일치하지만 이러한 전체 일치는 검사되지 않으므로 일치하는 것은 "쓰레기통"에 넣는 방법입니다.
그러나 원하는 콘텐츠는 그룹 1로 캡처됩니다. 그런 다음 프로그래밍 방식으로 그룹 1이 설정되어 있고 비어 있지 않은지 확인해야합니다. 이것은 사소한 프로그래밍 작업입니다 (그리고 나중에 어떻게 수행되는지에 대해 설명하겠습니다). 특히 한 눈에 이해하고 필요에 따라 수정하거나 확장 할 수있는 간단한 정규식을 남긴다는 점을 고려하면 더욱 그렇습니다.
나는 항상 시각화의 팬은 아니지만이 방법은 방법이 얼마나 간단한 지 잘 보여줍니다. 각 "라인"은 잠재적 인 일치 항목에 해당하지만 하단 라인 만 그룹 1로 캡처됩니다.
Debuggex 데모
Perl / PCRE 변형
위의 일반적인 솔루션과 달리, 적어도 @CasimiretHippolyte 및 @HamZa와 같은 정규식 신의 손에서 SO에서 자주 볼 수있는 Perl 및 PCRE의 변형이 있습니다. 그것은:
(?:s1|s2|s3)(*SKIP)(*F)|whatYouWant
귀하의 경우 :
(?m)(?:^.*\.$|\([^()]*\)|if\(.*?//endif)(*SKIP)(*F)|\b\d+\b
이 변형은 컨텍스트 s1, s2 및 s3에서 일치하는 콘텐츠를 건너 뛰기 때문에 사용하기가 조금 더 쉽습니다. 따라서 그룹 1 캡처를 검사 할 필요가 없습니다 (괄호가 사라짐에 유의). 일치 항목에는whatYouWant
그 주 (*F)
, (*FAIL)
그리고 (?!)
모두 같은 것입니다. 더 모호하게하고 싶다면 다음을 사용할 수 있습니다.(*SKIP)(?!)
이 버전의 데모
응용
다음은이 기술이 쉽게 해결할 수있는 몇 가지 일반적인 문제입니다. 단어 선택은 이러한 문제 중 일부가 실제로는 동일하지만 다르게 들리도록 만들 수 있습니다.
- 같은 태그의 아무 곳이나 제외하고 foo를 어떻게 일치시킬 수
<a stuff...>...</a>
있습니까?
<i>
태그 또는 자바 스크립트 스 니펫 (추가 조건)을 제외하고 foo를 어떻게 일치시킬 수 있습니까?
- 이 블랙리스트에없는 모든 단어를 어떻게 일치시킬 수 있습니까?
- SUB ... END SUB 블록 안의 내용을 어떻게 무시할 수 있습니까?
- s1 s2 s3를 제외한 모든 것을 어떻게 일치시킬 수 있습니까?
그룹 1 캡처를 프로그래밍하는 방법
당신은 코드가 아니라, 완성을 위해 ... 그룹 1을 검사하는 코드는 분명히 당신이 선택한 언어에 달려 있습니다. 어쨌든 일치를 검사하는 데 사용할 코드에 두 줄 이상을 추가해서는 안됩니다.
확실하지 않다면 앞서 언급 한 기사 의 코드 샘플 섹션 을 살펴볼 것을 권장합니다.이 섹션에서는 상당수의 언어에 대한 코드를 제공합니다.
대안
질문의 복잡성과 사용 된 정규식 엔진에 따라 몇 가지 대안이 있습니다. 다음은 여러 조건을 포함하여 대부분의 상황에 적용 할 수있는 두 가지입니다. 제 생각에는 s1|s2|s3|(whatYouWant)
명확성이 항상 승리하기 때문에 레시피 만큼 매력적이지 않습니다 .
1. 교체 후 일치.
이상하게 들리지만 많은 환경에서 잘 작동하는 좋은 솔루션은 두 단계로 작업하는 것입니다. 첫 번째 정규식은 잠재적으로 충돌 할 수있는 문자열을 대체하여 무시하려는 컨텍스트를 무력화합니다. 일치 만하려는 경우 빈 문자열로 바꾼 다음 두 번째 단계에서 일치를 실행할 수 있습니다. 교체하려면 먼저 무시할 문자열을 고유 한 것으로 교체 할 수 있습니다. 예를 들어 숫자를 고정 너비 체인 @@@
. 이 교체 후에는 원하는 것을 자유롭게 교체 할 수 있으며 고유 한 @@@
문자열 을 되돌려 야합니다 .
2. 둘러보기.
원본 게시물은 둘러보기를 사용하여 단일 조건을 제외하는 방법을 이해하고 있음을 보여주었습니다. 당신은 C #이 이것을 위해 훌륭하다고 말했고 당신이 옳다고 말했지만 이것이 유일한 옵션은 아닙니다. 예를 들어 C #, VB.NET 및 Visual C ++에서 찾을 수있는 .NET 정규식 버전과 Python에서 regex
대체 할 아직 실험적인 모듈 re
은 무한 너비 룩백을 지원하는 유일한 두 엔진입니다. 이러한 도구를 사용하면 하나의 룩 비하인드에서 하나의 조건으로 뒤를 볼 수있을뿐만 아니라 경기와 경기를 벗어난 모습을 처리 할 수 있으므로 룩어 헤드와 조정할 필요가 없습니다. 더 많은 조건? 더 많은 둘러보기.
C #에서 s3에 대한 정규식을 재활용하면 전체 패턴이 다음과 같이 보일 것입니다.
(?!.*\.)(?<!\([^()]*(?=\d+[^)]*\)))(?<!if\(\D*(?=\d+.*?//endif))\b\d+\b
하지만 지금 쯤이면 내가 이것을 추천하지 않는다는 것을 알죠?
삭제
@HamZa와 @Jerry는 삭제하려는 경우 추가 트릭을 언급 할 것을 제안했습니다 WhatYouWant
. 일치시킬 레시피 WhatYouWant
(그룹 1로 캡처)가 였음 을 기억 s1|s2|s3|(WhatYouWant)
하십니까? 의 모든 인스턴스를 삭제하려면 WhatYouWant
정규식을 다음과 같이 변경합니다.
(s1|s2|s3)|WhatYouWant
대체 문자열의 경우 $1
. 여기서 일어나는 s1|s2|s3
일은 일치하는 각 인스턴스에 대해 $1
교체가 해당 인스턴스를 자체로 대체한다는 것입니다 (에서 참조 $1
). 반면에 WhatYouWant
일치하는 경우 빈 그룹으로 교체되고 다른 항목은 삭제되므로 삭제됩니다. 이 데모를 참조하십시오 .이 멋진 추가 기능을 제안 해 주신 @HamZa 및 @Jerry에게 감사드립니다.
교체
이것은 우리에게 교체품을 가져 오며, 이에 대해서는 간단히 설명하겠습니다.
- 아무것도 바꾸지 않는 경우 위의 "삭제"트릭을 참조하십시오.
- 교체 할 때 Perl 또는 PCRE를 사용하는 경우
(*SKIP)(*F)
위에서 언급 한 변형을 사용하여 원하는 것과 정확히 일치하고 곧바로 교체하십시오.
- 다른 플레이버에서는 교체 함수 호출 내에서 콜백 또는 람다를 사용하여 일치를 검사하고 그룹 1이 설정된 경우 교체합니다. 이에 대한 도움이 필요하면 이미 참조 된 기사에서 다양한 언어로 된 코드를 제공합니다.
즐기세요!
아니, 잠깐만 더 있어요!
아, 아니, 내 회고록을 20 권으로 저장해 내년 봄에 공개하겠습니다.
\K
특별한 PHP 구문이 아닙니다. 당신이 말하고 싶은 것을 정교하고 명확하게하십시오. "복잡한"솔루션이 필요하지 않다고 말하는 것을 목표로한다면 무엇이 복잡한 지, 왜 그런지 말해야합니다.