Μεταβλητές αντιστοίχισης regex#

Μετά από κάθε επιτυχημένη αντιστοίχιση μοτίβου, η Perl συμπληρώνει ένα σταθερό σύνολο μεταβλητών με πληροφορίες για το τι ταίριαξε και πού. Αυτές είναι μόνο-ανάγνωσης - τις παρατηρείτε, δεν γράφετε σε αυτές. Η διευκρίνιση «επιτυχημένη» είναι κρίσιμη: μια ανεπιτυχής αντιστοίχιση αφήνει τις μεταβλητές να κρατούν ό,τι τις έθεσε η προηγούμενη επιτυχημένη αντιστοίχιση στην ίδια δυναμική εμβέλεια. Φιλτράρετε πάντα την πρόσβαση με το λογικό αποτέλεσμα της ίδιας της αντιστοίχισης.

Μεταβλητή

Κρατά

$1..$N

Κείμενο που συλλήφθηκε από την N-οστή ομάδα σύλληψης

$&

Ολόκληρη η υποσυμβολοσειρά που ταίριαξε

$`

Η συμβολοσειρά πριν την αντιστοίχιση

$'

Η συμβολοσειρά μετά την αντιστοίχιση

$+

Κείμενο που συλλήφθηκε από την ομάδα με τον μεγαλύτερο αριθμό

$^N

Κείμενο που συλλήφθηκε από την πιο πρόσφατα κλεισμένη ομάδα

@-

Offset αρχής: $-[0] = αντιστοίχιση, $-[N] = αρχή $N

@+

Offset τέλους: $+[0] = τέλος αντιστοίχισης, $+[N] = τέλος $N

%+

Hash ονομαστικών συλλήψεων: $+{name} = (?<name>...)

%-

Hash όλων των συλλήψεων κατά όνομα (τιμές arrayref)

@{^CAPTURE}

Πίνακας συλλήψεων: ${^CAPTURE}[0] = $1, κ.λπ.

${^MATCH}

Ίδιο με τη $&, συμπληρώνεται μόνο με τη σημαία /p

${^PREMATCH}

Ίδιο με τη $`, μόνο με /p

${^POSTMATCH}

Ίδιο με τη $', μόνο με /p

Το βασικό μοτίβο#

if ("Mr. Smith, age 47" =~ /(\w+)\s+(\w+),\s+age\s+(\d+)/) {
    print "title: $1\n";          # Mr
    print "name:  $2\n";          # Smith
    print "age:   $3\n";          # 47
    print "match: $&\n";          # the full match
}

Το if είναι αυτό που κάνει την πρόσβαση ασφαλή. Χωρίς αυτό, σε μια μη ταιριαστή συμβολοσειρά, οι $1/$& θα κρατούσαν ακόμα τιμές από κάποια προηγούμενη επιτυχημένη αντιστοίχιση - δεν υπάρχει κανόνας «αποτυχία αντιστοίχισης → καθαρισμός».

Αριθμημένες συλλήψεις - $1..$N#

Κάθε ομάδα σύλληψης (...) γεμίζει μία μεταβλητή. Η αρίθμηση είναι από αριστερά προς τα δεξιά βάσει αρχικής παρένθεσης:

"alpha-beta=42" =~ /^(\w+)-(\w+)=(\d+)$/;
print "$1 / $2 / $3\n";           # alpha / beta / 42

Οι μη συλλαμβάνουσες ομάδες (?:...) και οι διεκδικήσεις (?=...)/(?!...) δεν καταναλώνουν αριθμό· είναι αόρατες στην αρίθμηση.

Για αντικαταστάσεις s///, οι $1..$N είναι ορατές μέσα στην αντικατάσταση (μαζί με τις οπισθαναφορές τύπου $1 μόνο αντικατάστασης σε αντικαταστάσεις σε μονά εισαγωγικά):

my $s = "John Smith";
$s =~ s/(\w+)\s+(\w+)/$2, $1/;    # "Smith, John"

Ονομαστικές συλλήψεις - (?<name>...) και %+#

Οι ονομαστικές συλλήψεις είναι πιο ξεκάθαρες από τη μέτρηση παρενθέσεων, ειδικά σε μοτίβα με πολλές ομάδες:

my $log = "2024-03-15 14:32:01 ERROR connection refused";
if ($log =~ /^(?<date>\d{4}-\d{2}-\d{2})
              \s+
              (?<time>\d{2}:\d{2}:\d{2})
              \s+
              (?<level>\w+)
              \s+
              (?<msg>.*)$/x) {
    print "[$+{level}] $+{date} $+{time}: $+{msg}\n";
}

Η %+ είναι το hash ονομαστικών συλλήψεων. Οι αριθμημένες μορφές εξακολουθούν να λειτουργούν (οι ονομαστικές συλλήψεις λαμβάνουν επίσης αριθμούς με τη σειρά εμφάνισης), οπότε η $1 θα ήταν η $+{date} εδώ.

Η %- είναι παρόμοια αλλά οι τιμές της είναι αναφορές πίνακα, που κρατούν κάθε σύλληψη κάτω από αυτό το όνομα (σχετικό όταν χρησιμοποιούνται ομάδες επαναφοράς διακλάδωσης (?|...|...) ή όταν ένα όνομα επαναχρησιμοποιείται μεταξύ κλάδων εναλλαγής):

"abc" =~ /(?|(?<x>a)|(?<x>b)|(?<x>c))/;
print "%-{x} has @{$-{x}}\n";    # captured value(s) under name 'x'

Ο περισσότερος κώδικας διαβάζει μόνο τη %+. Η %- είναι για τις οριακές περιπτώσεις.

Όρια αντιστοίχισης - @- και @+#

Τα offset αρχής και τέλους της αντιστοίχισης στη συμβολοσειρά που ταίριαξε:

"hello world" =~ /(\w+)\s+(\w+)/;
print "match started at $-[0], ended at $+[0]\n";   # 0 .. 11
print "group 1: $-[1] .. $+[1]\n";                  # 0 .. 5
print "group 2: $-[2] .. $+[2]\n";                  # 6 .. 11

Οι $-[N] και $+[N] δίνουν τις ίδιες πληροφορίες που θα εξήγαγε η substr($var, $-[N], $+[N] - $-[N]) - είναι ο τρόπος ανακατασκευής θέσεων, όχι τιμών, μετά από αντιστοίχιση.

Η κλασική χρήση: αντικατάσταση υποσυμβολοσειρών που ταίριαξαν διατηρώντας το πρωτότυπο (χωρίς τον τελεστή s///):

my $s = "the quick brown fox";
$s =~ /quick (brown)/;
my $before = substr($s, 0, $-[0]);
my $after  = substr($s, $+[0]);
my $g1     = substr($s, $-[1], $+[1] - $-[1]);
print "$before|FOUND $g1|$after\n";

$&, $`, $' - αντιστοίχιση, πριν-αντιστοίχιση, μετά-αντιστοίχιση#

"hello world" =~ /wo\w+/;
print "before: '$`'\n";          # 'hello '
print "match:  '$&'\n";          # 'world'
print "after:  '$\''\n";         # ''

Αυτές οι τρεις είναι οι παλαιότερες μεταβλητές αντιστοίχισης της Perl και ιστορικά οι πιο ακριβές - δείτε Απόδοση: η ιστορία της $& παρακάτω.

Η $+ είναι το κείμενο που συλλήφθηκε από την ομάδα με τον μεγαλύτερο αριθμό που συμμετείχε στην αντιστοίχιση:

"abc" =~ /(a)(b)?(c)?/;          # $1='a', $2='b', $3='c', $+='c'
"a"   =~ /(a)(b)?(c)?/;          # $1='a', $2=undef, $3=undef, $+='a'

Η $^N είναι παρόμοια αλλά κρατά το κείμενο της πιο πρόσφατα κλεισμένης ομάδας - χρήσιμη μέσα σε σύνθετα μοτίβα που χρειάζονται την τιμή «της ομάδας που μόλις ολοκληρώθηκε»:

my $s = "tag:value";
$s =~ /(\w+):(\w+) (?{ $cb = $^N })/;
# $cb is the text most recently captured (here: "value")

Απόδοση: η ιστορία της $&#

Ιστορική επιφύλαξη που θα βρείτε σε παλαιό κώδικα και σε βιβλία:

Μην αναφέρετε τις $&, $`, ή $' πουθενά στον κώδικά σας, συμπεριλαμβανομένων αρθρωμάτων που κάνετε require - προκαλούν κάθε επιτυχημένη αντιστοίχιση να αντιγράψει ολόκληρη τη συμβολοσειρά που ταίριαξε, επιβραδύνοντας το πρόγραμμα.

Αυτό ίσχυε μέχρι την Perl 5.10. Από την Perl 5.18 και μετά, ο χρόνος εκτέλεσης παρακολουθεί ποια από τις τρεις μεταβλητές αναφέρει πραγματικά ο κώδικάς σας και αντιγράφει μόνο ό,τι χρειάζεται. Από την Perl 5.20 ένα σχήμα copy-on-write τις κάνει ουσιαστικά χωρίς κόστος.

Η PetaPerl ακολουθεί τη σύγχρονη συμπεριφορά: η $& και οι σχετικές είναι ασφαλείς για χρήση οπουδήποτε. Η σημαία /p και οι ${^MATCH} / ${^PREMATCH} / ${^POSTMATCH} υπάρχουν για την εποχή μεταξύ 5.10 και 5.20 όταν η αντιγραφή μέσω /p ήταν χρήσιμη βελτιστοποίηση. Δεν υπάρχει λόγος να γράψετε νέο κώδικα μαζί τους.

Εμβέλεια - είναι δυναμικά οριοθετημένες#

Οι μεταβλητές αντιστοίχισης δεν είναι λεξιλογικές· συμπεριφέρονται σαν δυναμικά οριοθετημένες μεταβλητές. Κάθε επιτυχημένη αντιστοίχιση κάνει local μια καθολική κατάσταση αντιστοίχισης στην τρέχουσα δυναμική εμβέλεια. Κρίσιμο:

  • Μια ανεπιτυχής αντιστοίχιση δεν καθαρίζει τις μεταβλητές.

  • Μια επιτυχημένη αντιστοίχιση μέσα σε εσωτερικό μπλοκ τις υπερισχύει, αλλά μόνο μέσα σε εκείνο το μπλοκ - όταν εξέλθει το εσωτερικό μπλοκ, η κατάσταση αντιστοίχισης της εξωτερικής εμβέλειας αποκαθίσταται.

"alpha" =~ /(\w+)/;              # $1 = 'alpha'
{
    "1234" =~ /(\d+)/;           # $1 = '1234' (inside this block)
    print "inner: $1\n";          # 1234
}
print "outer: $1\n";              # alpha - restored

Αυτό είναι περιστασιακά εκπληκτικό: μια συνάρτηση που καλείτε μέσα από μπλοκ χειρισμού regex δεν μολύνει τη $1 σας εκτός αν εκείνη η συνάρτηση κάνει δική της επιτυχημένη αντιστοίχιση στην ίδια δυναμική εμβέλεια (κάτι σπάνιο εκτός αν εκτελούνται στο ίδιο λεξιλογικό μπλοκ).

${^LAST_SUCCESSFUL_PATTERN}#

Μια αναφορά μόνο-ανάγνωσης προς το regex που παρήγαγε την τρέχουσα κατάσταση αντιστοίχισης - χρήσιμη για διαγνωστικά:

"hello" =~ /(\w+)/;
print "last pattern was: ${^LAST_SUCCESSFUL_PATTERN}\n";

Όταν αποτυγχάνουν οι αντιστοιχίσεις - pos#

Η θέση σε μια συμβολοσειρά μετά από αντιστοίχιση αγκυρωμένη με /g κρατείται όχι σε μία από τις μεταβλητές αυτής της σελίδας, αλλά στη pos:

my $s = "1 2 3 4";
while ($s =~ /(\d+)/g) {
    print "matched $1 at ", pos($s) - length($1), "\n";
}
# After the loop, pos($s) is undef.

Η pos είναι ανά συμβολοσειρά, ρυθμίσιμη, και είναι αυτό στο οποίο αγκυρώνεται η \G.

@{^CAPTURE} - οι συλλήψεις ως πίνακας#

Οι αριθμημένες συλλήψεις, εκτεθειμένες επίσης ως πίνακας μηδενοβάσει:

"alpha=42" =~ /(\w+)=(\d+)/;
print "name = ${^CAPTURE}[0]\n"; # 'alpha' (same as $1)
print "val  = ${^CAPTURE}[1]\n"; # '42'    (same as $2)
print "n    = scalar @{^CAPTURE}\n";  # 2

Αυτό είναι περιστασιακά ευκολότερο στην επανάληψη από τα $1, $2, , αλλά ο περισσότερος κώδικας χρησιμοποιεί τις αριθμημένες ή ονομαστικές μορφές απευθείας.

Δείτε επίσης#

  • m//, s/// - οι τελεστές που συμπληρώνουν κάθε μεταβλητή σε αυτή τη σελίδα.

  • qr// - μεταγλωττίζει ένα μοτίβο· το αντικείμενο regex που προκύπτει μπορεί αργότερα να αντιστοιχιστεί και να παράγει τις ίδιες συλλήψεις.

  • pos - το offset ανά συμβολοσειρά για /g και \G.

  • Σύνδεση regex - η =~, ο τελεστής που αποφασίζει σε ποια συμβολοσειρά εκτελείται η αντιστοίχιση.

  • Regular expressions guide

    • the regex language itself.

  • Groups and captures

    • the chapter on the capture variables.