2011-03-16 5 views
0

preg_match를 사용하여 다른 문이없는 경우 하나의 문을 캡처하는 방법을 알아 내려고합니다.PHP 정규식, 대체 문에서 첫 번째 그룹화를 무시하십시오.

샘플 텍스트 :

<!-- InstanceBeginEditable name="doctitle" --> 

<title>BU Libraries | Research Guides | Citing Your Sources</title> 

<!-- InstanceEndEditable --> 

<div id="standardpgt"><h1><!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable --></h1></div> 

PAGETITLE 내가 doctitle 태그 대신 당겨 할 존재 때문입니다. 물론 그들 사이에는 수많은 다른 문자가 있지만 작은 샘플을 보여주고 싶었습니다.

pagetitle이 없으면 doctitle의 콘텐츠를 가져오고 싶습니다.

PHP 코드를 직접 사용하지 않고 구성 파일을 통해 정규식 문을 전달하면 스크립트가이를 받아 들여 첫 번째 그룹을 명령문에서 꺼냅니다. 그것이 작동하지 않은 경우

문제가 어떤 이유로 PHP를 위해 무엇
((?!.*?<!--\s*?InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->.*?<!--\s*?InstanceEndEditable\s*?-->)<!--\s*?InstanceBeginEditable\s*?name=\x22doctitle\x22\s*?-->\s*?<title>(.*?)<\/title>\s*?<!--\s*?InstanceEndEditable\s*?-->|<!-- InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->(.*?)<!--\s*?InstanceEndEditable\s*?-->) 

항상 1 군으로 첫 번째 빈 그룹을 읽

이 내가 생각 해낸 것입니다.

0 -> <!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable --> 
1 -> 
2 -> <strong>Citing Your Sources</strong> 

을 반환 위의 샘플 텍스트의 예를 들어 내가 할 수없는이 일을하는 방법을 알아낼의 생활. 나는 또한이 정규 표현식을 썼다 :

(?(?=.*?<!--\s*?InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->.*?<!--\s*?InstanceEndEditable\s*?-->).*?<!-- InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->(.*?)<!--\s*?InstanceEndEditable\s*?-->|.*?<!--\s*?InstanceBeginEditable\s*?name=\x22doctitle\x22\s*?-->\s*?<title>(.*?)<\/title>\s*?<!--\s*?InstanceEndEditable\s*?-->) 

그러나 그것은 역시 작동하지 않았다. 도움을 주셔서 대단히 감사합니다.

크리스

+1

이것은 부수적으로 왜 [regexes로 HTML을 구문 분석하지 않아야하는지]입니다 (http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags)./1732454 # 1732454). 광기 외에 더 좋은 방법이 있습니다. 대신 정규식을 사용하는 시스템을 수정할 수 있습니까? 왜냐하면 심하게, 광기 때문에 ... – Charles

+0

편안한 의자를 찾아 랩톱을 꺼내서 [PHP DOMDocument] (http://www.php.net/manual/en/book.dom.php). –

+0

하나의 정규 표현식으로 사용하지 마십시오. – mhitza

답변

1

user178551 분기 리셋 구조체의 사용을 권유 올바른 절대적이다. 근본적으로 원래 정규식에는 아무런 문제가 없습니다 (길이가 300 자 이상이고 은 모두 한 줄에!입니다 - 하나의 캡처 그룹에 두 가지 대안 중 하나를 넣을 수 없음). 이것과 같이 정규 표현식을 사용하는 것은 쉽지 않다. 정규식을 읽을 수 있도록 들여 쓰기가 가능한 자유 간격 모드로 작성되어야한다. 여기에 몇 가지 합리적인 공백을 원래 정규식이 추가됩니다

$re_OP1 = '% 
    (           # $1: 
     (?! 
     .*?<!--\s*?InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?--> 
     .*?<!--\s*?InstanceEndEditable\s*?--> 
    ) 
      <!--\s*?InstanceBeginEditable\s*?name=\x22doctitle\x22\s*?-->\s*? 
      <title>(.*?)<\/title>\s*?    # $2: 
      <!--\s*?InstanceEndEditable\s*?--> 
    |  <!-- InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?--> 
      (.*?)         # $3; 
      <!--\s*?InstanceEndEditable\s*?--> 
    ) 
    %six'; 

, 당신은 당신이 열심히 OR 연산자 (즉 |<!-- InstanceBegin...)와 라인에 하나 개의 공간을 구분 한 위치를 확인할 수있는 지금이 정규식에서 찾고있다. 이렇게하면 정규식이 'x' 수정 자와 일치하지 않게됩니다. 그래서 \s*로이 공간을 교체하고 테스트 데이터를 실행하고, 여기에 내가 얻을 결과이다 (PHP-5.2.14) :

Array 
(
    [0] => <!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable --> 
    [1] => <!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable --> 
    [2] => 
    [3] => <strong>Citing Your Sources</strong> 
) 

이러한 결과는 당신이 게시 된 것과 유사하다 (그러나 대한 어떤 이유로 당신의 결과 만이 캡처 그룹을 ??? 쇼) 우리가 지금해야 할 일은 user178551의 지점 리셋 제안을 적용하고, 정규식 솔루션이된다 :

$re_jmr = '% 
    (?| # Branch reset construct. (restart counting for each alternative) 
     (?! 
     .*?<!--\s*InstanceBeginEditable\s*name="pagetitle"\s*--> 
     .*?<!--\s*InstanceEndEditable\s*--> 
    ) 
      <!--\s*InstanceBeginEditable\s*name="doctitle"\s*-->\s* 
      <title>(.*?)<\/title>\s*    # $1: Group 1A 
      <!--\s*InstanceEndEditable\s*--> 
    |  <!--\s*InstanceBeginEditable\s*name="pagetitle"\s*--> 
      (.*?)         # $1: Group 1B 
      <!--\s*InstanceEndEditable\s*--> 
    ) 
    %six'; 

내가 앞서 간 모든 변경했습니다 욕심 많은 게으른 \s*? (becau 욕심 많은 욕망이 여기에서 원하는 것입니다.) 또한 \x22을 모두 "으로 변경했습니다. 더 짧고 읽기 쉬운 IMHO입니다.

(I 틀리지 않는 경우)입니다
Array 
(
    [0] => <!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable --> 
    [1] => <strong>Citing Your Sources</strong> 
) 

정확하게 당신이 찾고있는 무엇을 : 그리고 여기이 새, 지점 리셋 정규식으로 실행 한 결과입니다. (당신은 다른 대안에 대한 테스트 케이스를 제공하지 않았으므로 아직 테스트되지 않았습니다.) 그 외에는 원래 정규식이 꽤 가깝습니다.

+0

당신은 완전합니다. 읽을 수 없었습니다. 내가 대신에 \ x22를 넣는 이유는 따옴표가 현재 PHP에 대한 정규식을 얻는 방법을 깬다는 것입니다. 현재 설정 파일을 읽는 스크립트이고 정규 표현식 patern은 받아 들일 수있는 설정 파일에서 가져옵니다 한 줄 설정. 우리는 현재 전체 프로세스를 다시 작성하는 중입니다. 엄청나게 비효율적입니다. 앞으로는 정규식 대신 xml 구문 분석을 사용하겠습니다.하지만이 훌륭한 설명을 해주셔서 정말 감사드립니다. 제비. – Chris

5

그냥 분기 리셋 패턴을 사용 : |

: "남자 perlre"에서

((?|(?!.*?<!--\s*?InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->.*?<!--\s*?InstanceEndEditable\s*?-->)<!--\s*?InstanceBeginEditable\s*?name=\x22doctitle\x22\s*?-->\s*?<title>(.*?)<\/title>\s*?<!--\s*?InstanceEndEditable\s*?-->|<!-- InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->(.*?)<!--\s*?InstanceEndEditable\s*?-->))s 

같이, 전체 표현의 주위에 (...?) "(? | pattern)" 이것은 "012"를 의미하는 "branch reset"패턴입니다. 캡쳐 버퍼는 이며 같은 시작 지점에서 번호가 매겨집니다.각각의 교대 지점에서. perl 5.10.0부터 사용할 수 있습니다.

캡처 버퍼는 왼쪽에서 오른쪽으로 번호가 매겨 지지만 이 구조 안에 번호 매기기는 각 분기마다 으로 다시 시작됩니다.

구조체 는 오직 한 지점을 포함하는 것처럼 각 지점 내의 번호가 정상적으로 될 것이며,이 구조는 다음의 어떤 ​​ 버퍼 번째되도록 그 안에 가장 캡쳐 버퍼와 하나 인.

개의 대체 성냥을 캡처하려는 경우 유용합니다.

다음 패턴을 고려하십시오. 아래의 숫자는 캡쳐 된 콘텐츠가 저장되는 버퍼를 보여줍니다.

  # before ---------------branch-reset----------- after 
     /(a) (?| x (y) z | (p (q) r) | (t) u (v)) (z) /x 
     # 1   2   2 3  2  3  4 
+0

이 답변은 "그룹 오버플로"(http://stackoverflow.com/a/22944075/2736496)의 [Stack Overflow Regular Expression FAQ]에 추가되었습니다. – aliteralmind

관련 문제