Μεταβλητές αντιστοίχισης regex#
Μετά από κάθε επιτυχημένη αντιστοίχιση μοτίβου, η Perl συμπληρώνει ένα σταθερό σύνολο μεταβλητών με πληροφορίες για το τι ταίριαξε και πού. Αυτές είναι μόνο-ανάγνωσης - τις παρατηρείτε, δεν γράφετε σε αυτές. Η διευκρίνιση «επιτυχημένη» είναι κρίσιμη: μια ανεπιτυχής αντιστοίχιση αφήνει τις μεταβλητές να κρατούν ό,τι τις έθεσε η προηγούμενη επιτυχημένη αντιστοίχιση στην ίδια δυναμική εμβέλεια. Φιλτράρετε πάντα την πρόσβαση με το λογικό αποτέλεσμα της ίδιας της αντιστοίχισης.
Μεταβλητή | Κρατά |
|---|---|
| Κείμενο που συλλήφθηκε από την N-οστή ομάδα σύλληψης |
| Ολόκληρη η υποσυμβολοσειρά που ταίριαξε |
| Η συμβολοσειρά πριν την αντιστοίχιση |
| Η συμβολοσειρά μετά την αντιστοίχιση |
| Κείμενο που συλλήφθηκε από την ομάδα με τον μεγαλύτερο αριθμό |
| Κείμενο που συλλήφθηκε από την πιο πρόσφατα κλεισμένη ομάδα |
| Offset αρχής: |
| Offset τέλους: |
| Hash ονομαστικών συλλήψεων: |
| Hash όλων των συλλήψεων κατά όνομα (τιμές arrayref) |
| Πίνακας συλλήψεων: |
| Ίδιο με τη |
| Ίδιο με τη |
| Ίδιο με τη |
Το βασικό μοτίβο#
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 - η
=~, ο τελεστής που αποφασίζει σε ποια συμβολοσειρά εκτελείται η αντιστοίχιση.-
the regex language itself.
-
the chapter on the capture variables.