2013-06-22 10 views
5

Szczerze, myślę, że najpierw powinienem poprosić o pomoc przy składni tego pytania.Jak utworzyć pętlę z wyrażeniem regularnym?

Ale proszę, jeśli rozumiesz, co mam na myśli, edytuj tytuł za pomocą odpowiedniego.

Czy istnieje sposób, aby utworzyć wzór, który może podzielić tekst w ten sposób.

{{START}} 
    {{START}} 
     {{START}} 
      {{START}} 
      {{END}} 
     {{END}} 
    {{END}} 
{{END}} 

Tak więc każde {{START}} pasuje do {{KONIEC}} od wewnątrz na zewnątrz jako ostatnie!

A jeśli nie mogę tego zrobić z tylko regex. A co z robieniem tego przy pomocy PHP?

Dziękuję z góry.

+5

Nie można tego zrobić z większością smaków regex, choć istnieją pewne sztuczki, poza moim ken, które umożliwiają to w językach takich jak Perl. Przeczytaj informacje na temat lematu pompowania, aby dowiedzieć się, dlaczego nie możesz tego zrobić. – siride

+0

Przypuszczam, że twoje formatowanie jakiegoś wejścia. Jeśli wytłumaczysz trochę więcej, być może zaproponujesz alternatywne podejście. –

+0

Wygląda na to, że próbujesz coś parsować ... [Jeśli coś jest w przybliżeniu tak skomplikowane, jak HTML (wygląda tak dla mnie), zrobienie tego za pomocą wyrażeń regularnych to zły pomysł.] (Http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454 # 1732454) – michaelb958

Odpowiedz

4

To wykracza poza możliwości wyrażenia regularnego, które może tylko parsować regularne gramatyki. To, co opisujesz, wymagałoby automatu odsuwania (języki standardowe są zdefiniowane przez regular automaton).

Możesz użyć wyrażenia regularnego do przeanalizowania poszczególnych elementów, ale część "głębia" musi być obsługiwana przez język z koncepcją pamięci (PHP w tym jest w porządku).

W twoim rozwiązaniu, wyrazy regularne będą używane tylko do identyfikacji twoich znaczników, podczas gdy prawdziwa logika co do głębokości śledzenia i określania, do którego elementu należy znacznik END, musi być twoim programem.

+1

PHP używa silnika regex, który może być czymś więcej niż zwykłymi wyrażeniami. http://pcre.org/pcre.txt - więc twoja odpowiedź jest tylko akademicka - nie ma praktycznego znaczenia. Jednak możesz również użyć tego silnika, aby zrobić, jak go zarysujesz. Pierwsza część nie dotyczy PHP/PCRE. – hakre

1

Nie można tego zrobić za pomocą czystej funkcji RegEx, jednak za pomocą prostej pętli można ją wykonać.

JS Przykład:

//[.\s\S]* ensures line breaks are matched (dotall not supported in JS) 
var exp = /\{\{START\}\}([.\s\S]*)\{\{END\}\}/; 

var myString = "{{START}}\ntest\n{{START}}\ntest 2\n{{START}}\ntest 3\n{{START}}\ntest4\n{{END}}\n{{END}}\n{{END}}\n{{END}}"; 

var matches = []; 
var m = exp.exec(myString); 
while (m != null) { 
    matches.push(m[0]); 
    m = exp.exec(m[1]); 
} 

alert(matches.join("\n\n")); 

PHP (nie mam pojęcia, czy to jest prawidłowe, to było zawsze odkąd robiłem PHP)

$pattern = "/\{\{START\}\}([.\s\S]*)\{\{END\}\}/"; 
$myString = "{{START}}\ntest\n{{START}}\ntest 2\n{{START}}\ntest 3\n{{START}}\ntest4\n{{END}}\n{{END}}\n{{END}}\n{{END}}"; 

$result = preg_match($pattern, $myString, $matches, PREG_OFFSET_CAPTURE); 
$outMatches = array(); 
while ($result) { 
    array_push($outMatches, $matches[0]); 
    $result = preg_match($pattern, $matches[1], $matches, PREG_OFFSET_CAPTURE); 
} 
print($outMatches); 

wyjściowa:

{{START}} 
test 
{{START}} 
test 2 
{{START}} 
test 3 
{{START}} 
test4 
{{END}} 
{{END}} 
{{END}} 
{{END}} 

{{START}} 
test 2 
{{START}} 
test 3 
{{START}} 
test4 
{{END}} 
{{END}} 
{{END}} 

{{START}} 
test 3 
{{START}} 
test4 
{{END}} 
{{END}} 

{{START}} 
test4 
{{END}} 
+0

OP chciał rozwiązania PHP. Spróbuj ponownie. – michaelb958

+0

Dodano PHP, nie mam pojęcia, czy jest poprawne czy nie. Nie robiłem PHP od lat. –

+0

W PHP regex ma rekursję, zobacz http://pcre.org/ – hakre

2

Jest to możliwe! Można mieć każdy poziom zawartości przy użyciu rekurencyjnego wyrażenia regularnego:

$data = <<<LOD 
{{START1}} 
    aaaaa 
    {{START2}} 
     bbbbb 
     {{START3}} 
      ccccc 
      {{START4}} 
       ddddd 
      {{END4}} 
     {{END3}} 
    {{END2}} 
{{END1}} 
LOD; 

$pattern = '~(?=({{START\d+}}(?>[^{]++|(?1))*{{END\d+}}))~'; 
preg_match_all ($pattern, $data, $matches); 

print_r($matches); 

Objaśnienia:

część: ({{START\d+}}(?>[^{]++|(?1))*{{END\d+}})

Ta część wzoru opisać zagnieżdżonej struktury z {{START#}} i {{END#}}

(   # open the first capturing group 
{{START\d+}} 
(?>   # open an atomic group (= backtracks forbidden) 
    [^{]++ # all that is not a { one or more times (possessive) 
    |   # OR 
    (?1)  # refer to the first capturing group itself 
)    # close the atomic group 
{END\d+}}  # 
)    # close the first capturing group 

Problem polega na tym, że nie można przechwycić całego poziomu tylko dla tej części, ponieważ wszystkie znaki ciągu są pochłaniane przez wzorzec. Innymi słowy, nie można dopasować nałożonych części ciągu.

Problem jest owinąć całą tę część wewnątrz zerowej szerokości twierdzenia, które nie zużywają postacie jak uprzedzona (?=...), wynik:

(?=({{START\d+}}(?>[^{]++|(?1))*{{END\d+}})) 

ten będzie pasował do wszystkich poziomów.

Powiązane problemy