it-swarm.com.de

Können mit regulären Ausdrücken verschachtelte Muster verglichen werden?

Ist es möglich, einen regulären Ausdruck zu schreiben, der mit einem verschachtelten Muster übereinstimmt, das in unbekannter Häufigkeit auftritt? Kann ein regulärer Ausdruck beispielsweise mit einer öffnenden und schließenden Klammer übereinstimmen, wenn eine unbekannte Anzahl von öffnenden/schließenden Klammern in den äußeren Klammern verschachtelt ist?

Beispielsweise:

public MyMethod()
{
  if (test)
  {
    // More { }
  }

  // More { }
} // End

Sollte passen:

{
  if (test)
  {
    // More { }
  }

  // More { }
}
222
Richard Dorman

So einfach ist das. Ein endlicher Automat (das ist die Datenstruktur, die einem regulären Ausdruck zugrunde liegt) hat außer dem Zustand, in dem er sich befindet, kein Gedächtnis. Wenn Sie eine willkürlich tiefe Verschachtelung haben, benötigen Sie einen willkürlich großen Automaten, der mit dem Begriff a kollidiert. -finite Automat.

Sie können verschachtelte/gepaarte Elemente bis zu einer festgelegten Tiefe zuordnen, wobei die Tiefe nur durch Ihren Speicher begrenzt ist, da der Automat sehr groß wird. In der Praxis sollten Sie jedoch einen Push-Down-Automaten verwenden, d. H. Einen Parser für eine kontextfreie Grammatik, z. B. LL (von oben nach unten) oder LR (von unten nach oben). Sie müssen das schlechtere Laufzeitverhalten berücksichtigen: O (n ^ 3) vs. O (n), mit n = Länge (Eingabe).

Es gibt viele Parser-Generatoren, zum Beispiel ANTLR für Java. Es ist auch nicht schwierig, eine vorhandene Grammatik für Java (oder C) zu finden.
Für weitere Hintergrundinformationen: Automata Theory bei Wikipedia

256
Torsten Marek

Die Suche nach verschachtelten Mustern mit regulären Ausdrücken ist sehr einfach.

'/(\((?>[^()]+|(?1))*\))/'
36
MichaelRushton

Wahrscheinlich funktionierende Perl-Lösung, wenn sich der String in einer Zeile befindet:

my $NesteD ;
$NesteD = qr/ \{( [^{}] | (??{ $NesteD }) )* \} /x ;

if ( $Stringy =~ m/\b( \w+$NesteD )/x ) {
    print "Found: $1\n" ;
  }

HTH

BEARBEITEN: überprüfen:

Und noch etwas von Torsten Marek (der richtig darauf hingewiesen hatte, dass es kein Regex mehr ist):

33
Zsolt Botykai

Ja, wenn es sich um eine .NET RegEx-Engine handelt. Die .Net-Engine unterstützt Finite-State-Maschinen, die mit einem externen Stack geliefert werden. siehe Einzelheiten

19
Pavlush

Das Pumplemma für reguläre Sprachen ist der Grund, warum Sie das nicht können.

Der generierte Automat hat eine endliche Anzahl von Zuständen, zum Beispiel k, so dass eine Folge von k + 1 öffnenden Klammern irgendwo wiederholt werden muss (während der Automat die Zeichen verarbeitet). Der Teil der Zeichenkette zwischen demselben Zustand kann unendlich oft dupliziert werden und der Automat wird den Unterschied nicht kennen.

Insbesondere wenn es k + 1 öffnende Klammern gefolgt von k + 1 schließenden Klammern akzeptiert (was es sollte), akzeptiert es auch die gepumpte Anzahl von öffnenden Klammern gefolgt von unveränderten k + 1 schließenden Klammern (was es nicht sollte).

15
Rafał Dowgird

Ordnungsgemäße reguläre Ausdrücke sind dazu nicht in der Lage, da Sie den Bereich der regulären Sprachen verlassen würden, um in den Gebieten der kontextfreien Sprachen zu landen.

Trotzdem sind die Pakete für "reguläre Ausdrücke", die viele Sprachen anbieten, strikt leistungsfähiger.

Beispiel: Lua reguläre Ausdrücke haben das Erkennungszeichen "%b()", das mit ausgeglichenen Klammern übereinstimmt. In Ihrem Fall würden Sie "%b{}" Verwenden.

Ein weiteres raffiniertes Werkzeug, das sed ähnelt, ist gema , mit dem Sie ausgeglichene geschweifte Klammern sehr einfach mit {#} Abgleichen können.

Je nachdem, über welche Tools Sie verfügen, kann Ihr "regulärer Ausdruck" (im weiteren Sinne) möglicherweise mit verschachtelten Klammern übereinstimmen.

13
Remo.D

Die Verwendung des rekursiven Abgleichs in der PHP Regex-Engine ist erheblich schneller als der prozedurale Abgleich von Klammern, insbesondere bei längeren Zeichenfolgen.

http://php.net/manual/en/regexp.reference.recursive.php

z.B.

$patt = '!\( (?: (?: (?>[^()]+) | (?R) )* ) \)!x';

preg_match_all( $patt, $str, $m );

vs.

matchBrackets( $str );

function matchBrackets ( $str, $offset = 0 ) {

    $matches = array();

    list( $opener, $closer ) = array( '(', ')' );

    // Return early if there's no match
    if ( false === ( $first_offset = strpos( $str, $opener, $offset ) ) ) {
        return $matches;
    }

    // Step through the string one character at a time storing offsets
    $paren_score = -1;
    $inside_paren = false;
    $match_start = 0;
    $offsets = array();

    for ( $index = $first_offset; $index < strlen( $str ); $index++ ) {
        $char = $str[ $index ];

        if ( $opener === $char ) {
            if ( ! $inside_paren ) {
                $paren_score = 1;
                $match_start = $index;
            }
            else {
                $paren_score++;
            }
            $inside_paren = true;
        }
        elseif ( $closer === $char ) {
            $paren_score--;
        }

        if ( 0 === $paren_score ) {
            $inside_paren = false;
            $paren_score = -1;
            $offsets[] = array( $match_start, $index + 1 );
        }
    }

    while ( $offset = array_shift( $offsets ) ) {

        list( $start, $finish ) = $offset;

        $match = substr( $str, $start, $finish - $start );
        $matches[] = $match;
    }

    return $matches;
}
5
Pete B

JA

... vorausgesetzt, es gibt eine maximale Anzahl von Nistplätzen, bei denen Sie gerne Halt machen würden.

Lassen Sie mich erklären.


@ torsten-marek ist richtig, dass ein regulärer Ausdruck nicht nach verschachtelten Mustern wie diesem suchen kann, [~ # ~] aber [~ # ~] Es ist möglich, zu definieren ein verschachteltes Regex-Muster zu erstellen, mit dem Sie verschachtelte Strukturen wie diese erfassen können bis zu einer gewissen maximalen Tiefe. Ich habe eine erstellt, um EBNF-style Kommentare ( probiere es hier aus ) zu erfassen, wie:

(* This is a comment (* this is nested inside (* another level! *) hey *) yo *)

Der reguläre Ausdruck (für Kommentare mit einer Tiefe) lautet wie folgt:

m{1} = \(+\*+(?:[^*(]|(?:\*+[^)*])|(?:\(+[^*(]))*\*+\)+

Dies kann leicht für Ihre Zwecke angepasst werden, indem Sie \(+\*+ Und \*+\)+ Durch { Und } Ersetzen und alles dazwischen durch einen einfachen [^{}]:

p{1} = \{(?:[^{}])*\}

( Hier ist der Link um das auszuprobieren.)

Zum Verschachteln lassen Sie dieses Muster einfach im Block selbst zu:

p{2} = \{(?:(?:p{1})|(?:[^{}]))*\}
  ...or...
p{2} = \{(?:(?:\{(?:[^{}])*\})|(?:[^{}]))*\}

Um dreifach verschachtelte Blöcke zu finden, verwenden Sie:

p{3} = \{(?:(?:p{2})|(?:[^{}]))*\}
  ...or...
p{3} = \{(?:(?:\{(?:(?:\{(?:[^{}])*\})|(?:[^{}]))*\})|(?:[^{}]))*\}

Es hat sich ein klares Muster herausgebildet. Um Kommentare zu finden, die bis zu einer Tiefe von N verschachtelt sind, verwenden Sie einfach den regulären Ausdruck:

p{N} = \{(?:(?:p{N-1})|(?:[^{}]))*\}

  where N > 1 and
  p{1} = \{(?:[^{}])*\}

Ein Skript könnte geschrieben werden, um diese regulären Ausdrücke rekursiv zu erzeugen, aber das geht über den Rahmen dessen, wofür ich das benötige. (Dies ist eine Übung für den Leser. ????)

4
awwsmm

wie Zsolt bereits erwähnte, unterstützen einige Regex-Engines die Rekursion. Dies sind natürlich normalerweise diejenigen, die einen Backtracking-Algorithmus verwenden, so dass dieser nicht besonders effizient ist. Beispiel: /(?>[^{}]*){(?>[^{}]*)(?R)*(?>[^{}]*)}/sm

Nein, an diesem Punkt betreten Sie das Reich der kontextfreien Grammatiken .

2
Craig H

Dies scheint zu funktionieren: /(\{(?:\{.*\}|[^\{])*\})/m

0
Sean Huber