7 Stimmen

Was bedeutet der reguläre Ausdruck .*? eigentlich?

Ich benutze seit einem Jahrzehnt Perl. Aber in letzter Zeit habe ich Probleme mit der Verwendung von .*? Regex.

Es scheint nicht die minimale Anzahl von Zeichen abzugleichen. Manchmal liefert es unterschiedliche Ergebnisse.

Zum Beispiel für diesen String: aaaaaaaaaaaaaaaaaaaaaaammmmmmmmmmmbaaaaaaaaaaaaaaaaaaaaaab und Muster: a.*?b es passt den kompletten Eingabestring in zwei Gruppen an. Laut Definition sollte es das letzte "ab" passen.

0 Stimmen

@Kobi - Ich glaube nicht, dass dies dieselbe Frage ist. Der OP fragt, warum .*? nicht immer die minimale Anzahl von Zeichen übereinstimmt, nicht was der Zweck des ? ist.

0 Stimmen

@Ted - gut genug, aber ich denke, wenn du verstehst wie .*? funktioniert du kannst diese Frage leicht beantworten. In diesem Fall könnte es eine Verallgemeinerung davon sein, vom selben Benutzer: stackoverflow.com/questions/5401571

0 Stimmen

Das hätte nicht die Antwort hervorgerufen, die ich für diese Frage suche. Deshalb habe ich einen neuen Thread gestartet, der aber verwandt ist.

13voto

ikegami Punkte 340842

Es sorgt nicht dafür, dass a.*?b die kleinste Anzahl an Zeichen übereinstimmt; es verursacht, dass .* die kleinste Anzahl an Zeichen übereinstimmt. Da es nur .* betrifft, hat es keine Auswirkung auf das, was bereits übereinstimmt (d.h. durch a).

Beispiel verkürzt auf:

#01234
'aaab' =~ /a.*?b/

Was passiert:

  1. Bei Position 0 passt a zu 1 Zeichen (a).
  2. Bei Position 1 passt .*? zu 0 Zeichen (leerer String).
  3. Bei Position 1 passt b nicht. Rückverfolgung
  4. Bei Position 1 passt .*? zu 1 Zeichen (a).
  5. Bei Position 2 passt b nicht. Rückverfolgung
  6. Bei Position 1 passt .*? zu 2 Zeichen (aa).
  7. Bei Position 3 passt b zu 1 Zeichen (b)
  8. Mustervergleich erfolgreich.

Wie Sie sehen können, hat es versucht, null Zeichen anzupassen, was offensichtlich die kleinste mögliche Übereinstimmung ist. Aber das Gesamtmuster konnte nicht übereinstimmen, als es das tat, also wurden größere und größere Übereinstimmungen ausprobiert, bis das Gesamtmuster übereinstimmte.


Ich versuche, den nicht-greedy Modifier zu vermeiden.

'aaab' =~ /a[^ab]*b/

Wenn a wirklich etwas Komplexeres ist, kann man ein negatives Lookahead verwenden.

'aaab' =~ /a(?:(?!a|b).)*b/

9voto

Tim Pietzcker Punkte 311448

Das bedeutet

.   # passt auf jedes Zeichen außer Zeilenumbrüche
*   # null oder mehrmals
?   # passt so wenig Zeichen wie möglich

Also in

 text  weiterer Text  noch mehr Text 

wird der reguläre Ausdruck (.*) den gesamten String auf einmal erfassen, einfangen

 text  weiterer Text  noch mehr Text 

in der Rückreferenz Nummer 1.

Wenn du das stattdessen mit (.*?) matchst, wirst du zwei Treffer bekommen:

  1. text
  2. noch mehr Text

wobei nur text und noch mehr Text jeweils in der Rückreferenz Nummer 1 erfasst werden.

Und wenn (danke Kobi!) dein Quelltext ist

 text  verschachtelter Text  zurück zur ersten Ebene 

dann wirst du feststellen, dass (.*) wieder den gesamten String matcht, aber (.*?) wird matchen

 text  verschachtelter Text 

weil der Regex-Engine von links nach rechts arbeitet. Das ist einer der Gründe, warum reguläre Ausdrücke "nicht das beste Werkzeug" zum Matchen von kontextfreien Grammatiken sind.

4voto

geekosaur Punkte 55917

Es passt die kleinste Anzahl von Zeichen zusammen, beginnend von der ersten Position, die übereinstimmen kann, was es dem Rest des Regex ermöglicht, übereinzustimmen. Der mittlere Teil davon (beginnend von ...) ist inhärent für die Art und Weise, wie die Regex-Statusmaschine funktioniert. (bearbeitet für weitere Klarstellung)

1voto

Ted Hopp Punkte 227177

Es soll die minimale Anzahl von Zeichen übereinstimmen, die erforderlich sind, damit das Muster als Ganzes erfolgreich passt (wenn überhaupt eine Übereinstimmung besteht). Können Sie ein spezifisches Beispiel nennen, bei dem dies nicht der Fall ist?

1voto

mirod Punkte 15573

Ich denke nicht, dass du ab direkt in deinem Fall finden kannst. Oft, wenn .*? nicht funktioniert, ruft es nach dem Muster [^c]*, wobei c ein Zeichen oder Zeichenklasse ist. Dies verhindert falsch positive Treffer

In diesem Fall funktioniert es jedoch nicht: a[^a]*b passt zuerst zu ammmmmmmmmmmb. Also der einzige Weg, um den kürzesten Treffer zu finden, ist, alle Treffer zu finden und dann den kürzesten auszuwählen.

Unten ist eine ausführliche (du hast gesagt, du hast schon lange nicht mehr mit Perl gearbeitet ;--) Methode, um zum gewünschten Ergebnis zu gelangen:

#!/usr/bin/perl 

use strict;
use warnings;

use List::Util qw(reduce); # Siehe List::Util-Dokumentation, was reduce macht

my $s= "aaaaaaaaaaaaaaaaaaaaaaammmmmmmmmmmbaaaaaaaaaaaaaaaaaaaaaab";

my $RE= qr/a[^a]*b/;

print "regexp: $RE\n";                 # ammmmmmmmmmmb
print "single match:\n";
if( $s=~ m{($RE)}) { print "  $1\n"; } 

print "all matches (loop):\n";         # ammmmmmmmmmmb \n ab
while( $s=~ m{($RE)}g)
  { print "  - $1\n"; }

print "all matches (in an array):\n";  # ammmmmmmmmmmb - ab
my @matches= $s=~ m{(a[^a]*b)}g;
if( @matches) { print "  ", join( " - ", @matches), "\n"; }

print "\nshortest match: ";            # ab
print reduce { length $a < length $b ? $a : $b } @matches;
print "\n";

Kurz gesagt: Lazy-Matching ist nicht dasselbe wie den kürzesten Treffer in einem String zu bekommen. Und diesen kürzesten Treffer zu bekommen ist kein einfaches Problem mit der Art von regex-Maschine, die Perl (und ich glaube die meisten anderen Sprachen) verwenden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X