[regex] 일치 할 정규식 패턴, 다음 경우 제외… / 다음 사이 제외

-편집 -현재 답변에는 유용한 아이디어가 있지만 100 % 이해하고 재사용 할 수있는 더 완전한 것을 원합니다. 그래서 현상금을 설정했습니다. 또한 어디에서나 작동하는 아이디어는 표준 구문이 아닌 것보다 나에게 더 좋습니다.\K

이 질문은 일부 상황 s1 s2 s3을 제외하고 패턴을 일치시킬 수있는 방법에 관한 것입니다. 내 의미를 보여주기 위해 구체적인 예를 들었지만 100 % 이해할 수있는 일반적인 답변을 선호하므로 다른 상황에서 재사용 할 수 있습니다.

\b\d{5}\b세 가지 상황이 아닌 s1 s2 s3을 사용하여 다섯 자리를 일치시키고 싶습니다 .

s1 : 이 문장처럼 마침표로 끝나는 줄에 있지 않습니다.

s2 : 괄호 안에는 어디에도 없습니다.

s3 : 다음으로 시작 if(하고 끝나는 블록 내부가 아닙니다 .//endif

나는 s1 s2 s3 중 하나를 예견과 예견으로, 특히 C # lookbehind 또는 \KPHP에서 해결하는 방법을 알고 있습니다.

예를 들어

s1 (?m)(?!\d+.*?\.$)\d+

C # lookbehind를 사용하는 s3 (?<!if\(\D*(?=\d+.*?//endif))\b\d+\b

PHP \ K가있는 s3 (?:(?:if\(.*?//endif)\D*)*\K\d+

하지만 여러 조건이 섞여서 머리가 터집니다. 더 나쁜 소식은 다른 조건 s4 s5를 다른 시간에 추가해야 할 수도 있다는 것입니다.

좋은 소식은 PHP, C #, Python 또는 이웃 세탁기와 같은 가장 일반적인 언어를 사용하여 파일을 처리해도 상관 없다는 것입니다. 🙂 저는 Python 및 Java의 초보자이지만 솔루션이 있는지 배우고 싶습니다.

그래서 나는 누군가가 유연한 레시피를 생각하는지 알아보기 위해 여기에 왔습니다.

힌트는 괜찮습니다. 전체 코드를 제공 할 필요가 없습니다. 🙂

감사합니다.



답변

한스, 나는 미끼를 가져다가 내 이전 대답을 구체화하겠습니다. “좀 더 완전한 것”을 원한다고 하셨기 때문에 긴 대답이 마음에 들지 않으시 길 바랍니다. 몇 가지 배경 지식부터 시작하겠습니다.

우선, 이것은 훌륭한 질문입니다. 특정 컨텍스트 (예 : 코드 블록 또는 괄호 내부)를 제외하고 특정 패턴 일치에 대한 질문이 자주 있습니다. 이러한 질문은 종종 상당히 어색한 해결책을 제시합니다. 따라서 여러 상황 에 대한 귀하의 질문은 특별한 도전입니다.

놀라다

놀랍게도 일반적이고 구현하기 쉬우 며 유지 관리하기 좋은 효율적인 솔루션이 하나 이상 있습니다. 그것은 모든 정규식 맛을 작동 코드에서 캡처 그룹을 검사 할 수 있습니다. 그리고 처음에는 당신과 다르게 들릴 수있는 몇 가지 일반적인 질문에 대답합니다. “도너츠를 제외한 모든 항목 일치”, “모두 교체 …”, “엄마의 블랙리스트에있는 단어를 제외한 모든 단어 일치”, “무시” 태그 “,”기울임 꼴이 아닌 경우 온도 일치 “…

슬프게도이 기술은 잘 알려져 있지 않습니다. 나는 그것을 사용할 수있는 20 개의 SO 질문 중 하나만이 그것을 언급하는 하나의 답을 가지고 있다고 추정합니다. 이는 아마도 50 개 또는 60 개의 답 중 하나를 의미합니다. 코멘트에서 Kobi와의 교환을 참조하십시오. 이 기술은 (낙관적으로) “역대 최고의 정규식 트릭”이라고 부르는 이 기사 에서 자세히 설명 합니다. 자세히 설명하지 않고 기술이 어떻게 작동하는지 확실히 이해하려고 노력할 것입니다. 다양한 언어로 된 자세한 내용과 코드 샘플을 보려면 해당 리소스를 참조하십시오.

더 잘 알려진 변형

동일한 작업을 수행하는 Perl 및 PHP에 특정한 구문을 사용하는 변형이 있습니다. CasimiretHippolyteHamZa 와 같은 정규식 마스터의 손에서 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)(?!)

이 버전의 데모

응용

다음은이 기술이 쉽게 해결할 수있는 몇 가지 일반적인 문제입니다. 단어 선택은 이러한 문제 중 일부가 실제로는 동일하지만 다르게 들리도록 만들 수 있습니다.

  1. 같은 태그의 아무 곳이나 제외하고 foo를 어떻게 일치시킬 수 <a stuff...>...</a>있습니까?
  2. <i>태그 또는 자바 스크립트 스 니펫 (추가 조건)을 제외하고 foo를 어떻게 일치시킬 수 있습니까?
  3. 이 블랙리스트에없는 모든 단어를 어떻게 일치시킬 수 있습니까?
  4. SUB … END SUB 블록 안의 내용을 어떻게 무시할 수 있습니까?
  5. 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에게 감사드립니다.

교체

이것은 우리에게 교체품을 가져 오며, 이에 대해서는 간단히 설명하겠습니다.

  1. 아무것도 바꾸지 않는 경우 위의 “삭제”트릭을 참조하십시오.
  2. 교체 할 때 Perl 또는 PCRE를 사용하는 경우 (*SKIP)(*F)위에서 언급 한 변형을 사용하여 원하는 것과 정확히 일치하고 곧바로 교체하십시오.
  3. 다른 플레이버에서는 교체 함수 호출 내에서 콜백 또는 람다를 사용하여 일치를 검사하고 그룹 1이 설정된 경우 교체합니다. 이에 대한 도움이 필요하면 이미 참조 된 기사에서 다양한 언어로 된 코드를 제공합니다.

즐기세요!

아니, 잠깐만 더 있어요!

아, 아니, 내 회고록을 20 권으로 저장해 내년 봄에 공개하겠습니다.


답변

세 가지 다른 일치를 수행하고 프로그램 내 조건 논리를 사용하여 세 가지 상황의 조합을 처리합니다. 하나의 거대한 정규식으로 모든 것을 처리 할 필요는 없습니다.

편집 : 질문이 더 흥미로워 졌기 때문에 조금 확장하겠습니다 🙂

여기서 캡처하려는 일반적인 아이디어는 특정 정규식 패턴과 일치시키는 것이지만 테스트 문자열에 특정 다른 (숫자가 될 수 있음) 패턴이있는 경우에는 그렇지 않습니다. 다행히도 프로그래밍 언어를 활용할 수 있습니다. 정규식을 단순하게 유지하고 복합 조건을 사용하십시오. 이 아이디어를 재사용 가능한 구성 요소에 캡처하는 것이 가장 좋은 방법이므로이를 구현하는 클래스와 메서드를 만들어 보겠습니다.

using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

public class MatcherWithExceptions {
  private string m_searchStr;
  private Regex m_searchRegex;
  private IEnumerable<Regex> m_exceptionRegexes;

  public string SearchString {
    get { return m_searchStr; }
    set {
      m_searchStr = value;
      m_searchRegex = new Regex(value);
    }
  }

  public string[] ExceptionStrings {
    set { m_exceptionRegexes = from es in value select new Regex(es); }
  }

  public bool IsMatch(string testStr) {
    return (
      m_searchRegex.IsMatch(testStr)
      && !m_exceptionRegexes.Any(er => er.IsMatch(testStr))
    );
  }
}

public class App {
  public static void Main() {
    var mwe = new MatcherWithExceptions();

    // Set up the matcher object.
    mwe.SearchString = @"\b\d{5}\b";
    mwe.ExceptionStrings = new string[] {
      @"\.$"
    , @"\(.*" + mwe.SearchString + @".*\)"
    , @"if\(.*" + mwe.SearchString + @".*//endif"
    };

    var testStrs = new string[] {
      "1." // False
    , "11111." // False
    , "(11111)" // False
    , "if(11111//endif" // False
    , "if(11111" // True
    , "11111" // True
    };

    // Perform the tests.
    foreach (var ts in testStrs) {
      System.Console.WriteLine(mwe.IsMatch(ts));
    }
  }
}

따라서 위에서 검색 문자열 (5 자리 숫자), 여러 예외 문자열 ( s1 , s2s3 )을 설정 한 다음 여러 테스트 문자열과 일치 시키려고합니다. 인쇄 된 결과는 각 테스트 문자열 옆의 주석에 표시되어야합니다.


답변

괄호 안에 있지 않다는 귀하의 요구 사항은 모든 경우에 대해 만족시킬 수 없습니다. 즉, 어떻게 든 (왼쪽과 )오른쪽에서 a 를 찾을 수 있다고 해서 항상 괄호 안에 있다는 의미는 아닙니다. 예 :

(....) + 55555 + (.....)-하지 괄호 안에 아직 (하고 )왼쪽과 오른쪽에

이제 자신이 영리하다고 생각하고 이전에 (만나지 않은 경우에만 왼쪽을 찾고 )오른쪽에서 그 반대를 찾을 수 있습니다. 이 경우에는 작동하지 않습니다.

((.....) + 55555 + (.....))– 내부가 폐쇄 되더라도 괄호 )(왼쪽으로 오른쪽으로.

정규식은 열린 괄호 수와 닫은 괄호 수를 계산할 수 없기 때문에 정규식을 사용하여 괄호 안에 있는지 알아내는 것은 불가능합니다.

모든위한 그 문자열의 모든 (가능성이 중첩) 괄호가 닫혀 있는지 확인, 정규식을 사용하여이 쉽게 작업을 고려 (하면 찾을 필요 ). 해결이 불가능하다는 것을 알게 될 것이며 정규식으로 해결할 수 없다면 모든 경우에 대해 괄호 안에 단어가 있는지 알아낼 수 없습니다. 모든 선행 (에는 해당하는 ).


답변

Hans 괜찮다면 이웃의 세탁기 인 perl을 사용했습니다. 🙂

수정 됨 :
의사 코드 아래 :

  loop through input
  if line contains 'if(' set skip=true
        if skip= true do nothing
        else
           if line match '\b\d{5}\b' set s0=true
           if line does not match s1 condition  set s1=true
           if line does not match s2 condition  set s2=true
           if s0,s1,s2 are true print line 
  if line contains '//endif' set skip=false

input.txt 파일이 주어지면 :

tiago@dell:~$ cat input.txt 
this is a text
it should match 12345
if(
it should not match 12345
//endif 
it should match 12345
it should not match 12345.
it should not match ( blabla 12345  blablabla )
it should not match ( 12345 )
it should match 12345

그리고 스크립트 validator.pl :

tiago@dell:~$ cat validator.pl 
#! /usr/bin/perl
use warnings;
use strict;
use Data::Dumper;

sub validate_s0 {
    my $line = $_[0];
    if ( $line =~ \d{5/ ){
        return "true";
    }
    return "false";
}

sub validate_s1 {
    my $line = $_[0];
    if ( $line =~ /\.$/ ){
        return "false";
    }
    return "true";
}

sub validate_s2 {
    my $line = $_[0];
    if ( $line =~ /.*?\(.*\d{5.*?\).*/ ){
        return "false";
    }
    return "true";
}

my $skip = "false";
while (<>){
    my $line = $_; 

    if( $line =~ /if\(/ ){
       $skip = "true";  
    }

    if ( $skip eq "false" ) {
        my $s0_status = validate_s0 "$line"; 
        my $s1_status = validate_s1 "$line";
        my $s2_status = validate_s2 "$line";

        if ( $s0_status eq "true"){
            if ( $s1_status eq "true"){
                if ( $s2_status eq "true"){
                    print "$line";
                }
            }
        }
    } 

    if ( $line =~ /\/\/endif/) {
        $skip="false";
    }
}

실행:

tiago @ dell : ~ $ cat input.txt | 펄 validator.pl
12345와 일치해야합니다.
12345와 일치해야합니다.
12345와 일치해야합니다.


답변

이것이 도움이 될지 여부는 확실하지 않지만 다음 가정을 고려하여 솔루션을 제공하고 있습니다.

  1. 모든 조건을 확인하려면 우아한 솔루션이 필요합니다.
  2. 조건은 미래 및 언제든지 변경 될 수 있습니다.
  3. 한 조건이 다른 조건에 의존해서는 안됩니다.

그러나 다음도 고려했습니다.

  1. 주어진 파일에는 최소한의 오류가 있습니다. 그렇다면 내 코드가 이에 대처하기 위해 약간의 수정이 필요할 수 있습니다.
  2. 스택을 사용하여 if(블록을 추적했습니다 .

여기에 해결책이 있습니다.

C #과 함께 MEF (Microsoft Extensibility Framework)를 사용하여 구성 가능한 파서를 구현했습니다. 아이디어는 구문 분석에 단일 파서를 사용하고 구성 가능한 유효성 검사기 클래스 목록을 사용하여 줄을 확인하고 유효성 검사에 따라 true 또는 false를 반환하는 것입니다. 그런 다음 언제든지 유효성 검사기를 추가 또는 제거하거나 원하는 경우 새 유효성 검사기를 추가 할 수 있습니다. 지금까지 말씀 드린 S1, S2 및 S3에 대해 이미 구현했습니다. 포인트 3에서 클래스를 확인하십시오. 나중에 필요하면 s4, s5에 대한 클래스를 추가해야합니다.

  1. 먼저 인터페이스 만들기-

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace FileParserDemo.Contracts
    {
        public interface IParser
        {
            String[] GetMatchedLines(String filename);
        }
    
        public interface IPatternMatcher
        {
            Boolean IsMatched(String line, Stack<string> stack);
        }
    }
  2. 그런 다음 파일 판독기와 검사기가 나타납니다.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using FileParserDemo.Contracts;
    using System.ComponentModel.Composition.Hosting;
    using System.ComponentModel.Composition;
    using System.IO;
    using System.Collections;
    
    namespace FileParserDemo.Parsers
    {
        public class Parser : IParser
        {
            [ImportMany]
            IEnumerable<Lazy<IPatternMatcher>> parsers;
            private CompositionContainer _container;
    
            public void ComposeParts()
            {
                var catalog = new AggregateCatalog();
                catalog.Catalogs.Add(new AssemblyCatalog(typeof(IParser).Assembly));
                _container = new CompositionContainer(catalog);
                try
                {
                    this._container.ComposeParts(this);
                }
                catch
                {
    
                }
            }
    
            public String[] GetMatchedLines(String filename)
            {
                var matched = new List<String>();
                var stack = new Stack<string>();
                using (StreamReader sr = File.OpenText(filename))
                {
                    String line = "";
                    while (!sr.EndOfStream)
                    {
                        line = sr.ReadLine();
                        var m = true;
                        foreach(var matcher in this.parsers){
                            m = m && matcher.Value.IsMatched(line, stack);
                        }
                        if (m)
                        {
                            matched.Add(line);
                        }
                     }
                }
                return matched.ToArray();
            }
        }
    }
  3. 그런 다음 개별 검사기가 구현되고 클래스 이름은 자명하므로 더 많은 설명이 필요하지 않다고 생각합니다.

    using FileParserDemo.Contracts;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Threading.Tasks;
    
    namespace FileParserDemo.PatternMatchers
    {
        [Export(typeof(IPatternMatcher))]
        public class MatchAllNumbers : IPatternMatcher
        {
            public Boolean IsMatched(String line, Stack<string> stack)
            {
                var regex = new Regex("\\d+");
                return regex.IsMatch(line);
            }
        }
    
        [Export(typeof(IPatternMatcher))]
        public class RemoveIfBlock : IPatternMatcher
        {
            public Boolean IsMatched(String line, Stack<string> stack)
            {
                var regex = new Regex("if\\(");
                if (regex.IsMatch(line))
                {
                    foreach (var m in regex.Matches(line))
                    {
                        //push the if
                        stack.Push(m.ToString());
                    }
                    //ignore current line, and will validate on next line with stack
                    return true;
                }
                regex = new Regex("//endif");
                if (regex.IsMatch(line))
                {
                    foreach (var m in regex.Matches(line))
                    {
                        stack.Pop();
                    }
                }
                return stack.Count == 0; //if stack has an item then ignoring this block
            }
        }
    
        [Export(typeof(IPatternMatcher))]
        public class RemoveWithEndPeriod : IPatternMatcher
        {
            public Boolean IsMatched(String line, Stack<string> stack)
            {
                var regex = new Regex("(?m)(?!\\d+.*?\\.$)\\d+");
                return regex.IsMatch(line);
            }
        }
    
    
        [Export(typeof(IPatternMatcher))]
        public class RemoveWithInParenthesis : IPatternMatcher
        {
            public Boolean IsMatched(String line, Stack<string> stack)
            {
                var regex = new Regex("\\(.*\\d+.*\\)");
                return !regex.IsMatch(line);
            }
        }
    }
  4. 프로그램 –

    using FileParserDemo.Contracts;
    using FileParserDemo.Parsers;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace FileParserDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                var parser = new Parser();
                parser.ComposeParts();
                var matches = parser.GetMatchedLines(Path.GetFullPath("test.txt"));
                foreach (var s in matches)
                {
                    Console.WriteLine(s);
                }
                Console.ReadLine();
            }
        }
    }

테스트를 Test.txt위해 다음 줄이있는 @Tiago의 샘플 파일을 가져 왔습니다.

this is a text
it should match 12345
if(
it should not match 12345
//endif 
it should match 12345
it should not match 12345.
it should not match ( blabla 12345  blablabla )
it should not match ( 12345 )
it should match 12345

출력을 제공합니다-

it should match 12345
it should match 12345
it should match 12345

이것이 당신에게 도움이 될지 모르겠지만, 나는 그것을 가지고 노는 즐거운 시간을 보냈습니다 …. 🙂

그것의 가장 좋은 부분은, 새로운 조건을 추가하기 위해 당신이해야 할 일은의 구현을 제공하는 IPatternMatcher것 뿐이며, 자동으로 호출되어 유효성을 검사한다는 것입니다.


답변

@ zx81과 동일 (*SKIP)(*F)하지만 부정적인 예견 어설 션을 사용합니다.

(?m)(?:if\(.*?\/\/endif|\([^()]*\))(*SKIP)(*F)|\b\d+\b(?!.*\.$)

데모

파이썬에서는 이렇게 쉽게 할 수 있습니다.

import re
string = """cat 123 sat.
I like 000 not (456) though 111 is fine
222 if(  //endif if(cat==789 stuff  //endif   333"""
for line in string.split('\n'):                                  # Split the input according to the `\n` character and then iterate over the parts.
    if not line.endswith('.'):                                   # Don't consider the part which ends with a dot.
        for i in re.split(r'\([^()]*\)|if\(.*?//endif', line):   # Again split the part by brackets or if condition which endswith `//endif` and then iterate over the inner parts.
            for j in re.findall(r'\b\d+\b', i):                  # Then find all the numbers which are present inside the inner parts and then loop through the fetched numbers.
                print(j)                                         # Prints the number one ny one.

산출:

000
111
222
333


답변