Ομάδες και συλλήψεις#

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

/house(cat|keeper)/;       # 'house' followed by 'cat' or 'keeper'
/(ab){3}/;                 # 'ababab'
/(\d{3})-(\d{4})/;         # capture two groups separated by '-'

Πέρα από την απλή συλλαμβάνουσα μορφή (…), η Perl παρέχει έναν μικρό ζωολογικό κήπο κατασκευών σε παρενθέσεις: μη συλλαμβάνουσες ομάδες για καθαρή ομαδοποίηση, ονομαστικές συλλήψεις για αυτο-τεκμηριωμένα μοτίβα, ατομικές ομάδες για έλεγχο της οπισθοχώρησης, αναδρομικά υπομοτίβα για εμφωλευμένες δομές, υπό συνθήκη μοτίβα για διακλάδωση με βάση προηγούμενες συλλήψεις, και την κατασκευή επαναφοράς διακλάδωσης για παράλληλες εναλλακτικές. Αυτό το κεφάλαιο τις καλύπτει όλες.

Συλλαμβάνουσες ομάδες: $1, $2, …#

Κάθε ζεύγος μη διαφεμένων παρενθέσεων σε ένα μοτίβο ανοίγει μια συλλαμβάνουσα ομάδα. Μετά από μια επιτυχημένη αντιστοίχιση, το ταιριασμένο κείμενο της n-οστής ομάδας βρίσκεται στο $n:

if ($time =~ /(\d\d):(\d\d):(\d\d)/) {
    my ($hours, $minutes, $seconds) = ($1, $2, $3);
}

Σε περιβάλλον λίστας, μια αντιστοίχιση επιστρέφει απευθείας τη λίστα των συλληφθεισών συμβολοσειρών:

my ($h, $m, $s) = $time =~ /(\d\d):(\d\d):(\d\d)/;

Αν το μοτίβο αποτύχει, η λίστα είναι κενή — ένας χρήσιμος ιδιωματισμός για «ανάλυσε ή παραιτήσου»:

my ($h, $m, $s) = $time =~ /(\d\d):(\d\d):(\d\d)/
    or die "not a time: $time";

Δεν υπάρχει άνω όριο στο πλήθος των ομάδων σύλληψης. Οι ομάδες αριθμούνται με την πιο αριστερή ανοιχτή παρένθεση ως ομάδα 1, την επόμενη ως ομάδα 2, κ.ο.κ.:

/(ab(cd|ef)((gi)|j))/
  1  2      34

Το $1 συλλαμβάνει την εξωτερική ομάδα, το $2 την πρώτη εσωτερική, το $3 την επόμενη, το $4 την πιο εσωτερική.

Συνέλαβε την κενή συμβολοσειρά έναντι δεν ταίριαξε καθόλου#

Μια ομάδα σύλληψης που δεν συμμετείχε στην αντιστοίχιση έχει το $n απροσδιόριστο. Μια ομάδα που συμμετείχε και ταίριαξε με κενή συμβολοσειρά έχει το $n ορισμένο και ίσο με "". Η διάκριση έχει σημασία:

"aba" =~ / a (x)* b \g1 a /x;   # does NOT match
"aba" =~ / a (x)? b \g1 a /x;   # does NOT match
"aba" =~ / a (x*) b \g1 a /x;   # matches; $1 = ""
"aba" =~ / a (x?) b \g1 a /x;   # matches; $1 = ""

Στις δύο πρώτες περιπτώσεις, ο ποσοδείκτης είναι εκτός της ομάδας, οπότε η ίδια η ομάδα ποτέ δεν έκλεισε (η μηχανή ταίριαξε μηδέν επαναλήψεις — η ομάδα δεν εισήλθε καθόλου). Η οπισθαναφορά \g1 επομένως δεν έχει τιμή για σύγκριση και αποτυγχάνει. Στις δύο δεύτερες περιπτώσεις, ο ποσοδείκτης είναι μέσα στην ομάδα, οπότε η ομάδα έτρεξε ακριβώς μία φορά και συνέλαβε μια κενή συμβολοσειρά· το \g1 ταιριάζει με την ίδια κενή συμβολοσειρά σε εκείνη τη θέση.

Το μάθημα: όταν μια ομάδα είναι προαιρετική, βάλτε τον ποσοδείκτη μέσα αν θέλετε τη σημασιολογία «η κενή αντιστοιχία μετράει ως αντιστοιχία».

Ελέγχετε πάντα τις συλλήψεις με defined, όχι για αλήθεια — μια κενή σύλληψη είναι αληθής-αλλά-κενή υπό τη σημασιολογία συμβολοσειρών:

if ("x" =~ /(a)?(x)/) {
    print "1 is $1\n" if defined $1;   # $1 is undef here
    print "2 is $2\n" if defined $2;
}

Οι αποτυχημένες αντιστοιχίσεις δεν επαναφέρουν τις μεταβλητές σύλληψης#

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

"foo" =~ /(\w+)/ and "" =~ /(\d+)/;   # second fails
print $1;   # "foo" — first match's capture survives

Είναι επίσης μια συχνή πηγή σύγχυσης. Ελέγχετε πάντα την τιμή επιστροφής της αντιστοίχισης πριν διαβάσετε τις μεταβλητές σύλληψης.

Μη συλλαμβάνουσες ομάδες: (?:…)#

Αν χρειάζεστε την ομαδοποίηση μόνο για ποσοδεικτοποίηση ή εναλλαγή, και δεν θέλετε τη σύλληψη, χρησιμοποιήστε (?:…):

/(?:ab){3}/;            # 'ababab', no capture
/(?:\d+\.)*\d+/;        # a dotted decimal, no captures at all

Οι μη συλλαμβάνουσες ομάδες είναι μικρή νίκη σε ταχύτητα και μεγαλύτερη νίκη σε σαφήνεια. Σηματοδοτούν «αυτή η ομαδοποίηση είναι για σύνταξη, όχι για δεδομένα». Επίσης αποτρέπουν την επαναρίθμηση των συλλαμβανουσών ομάδων που σας ενδιαφέρουν:

# match a number — $1 = whole, $2 = optional exponent value
/([+-]?\ *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE]([+-]?\d+))?)/;

Χωρίς τα περιτυλίγματα (?:…), τα $2, $3, $4 θα ορίζονταν όλα και το επιθυμητό $2 (ο εκθέτης) θα μετατοπιζόταν στο $5.

Η μορφή με εμβέλεια (?flags:…) (π.χ. (?i:cat), (?xms:…)) προσαρτά τροποποιητές μόνο στο εσωτερικό μοτίβο και είναι επίσης μη συλλαμβάνουσα — δείτε το κεφάλαιο modifiers.

Το split επωφελείται επίσης από το (?:…). Το split /(?:\s+)/ διαχωρίζει σε ακολουθίες λευκών χαρακτήρων χωρίς να εισάγει τους διαχωριστές στην έξοδο· το split /(\s+)/ τους αφήνει σε εναλλασσόμενες θέσεις.

Ονομαστικές συλλήψεις#

Το (?<name>…) ή το (?'name'…) ονοματίζει μια ομάδα. Η αντιστοιχία της είναι προσβάσιμη μέσω του κατακερματισμού %+:

if ("2026-04-23" =~ /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/) {
    print "year = $+{year}\n";    # 2026
    print "month = $+{month}\n";  # 04
    print "day = $+{day}\n";      # 23
}

Οι ονομαστικές ομάδες επίσης γεμίζουν τα $1, $2, … με τη συνηθισμένη σειρά από αριστερά προς τα δεξιά, οπότε κώδικας που χρησιμοποιεί και τις δύο συμβάσεις λειτουργεί. Μέσα στο μοτίβο, αναφερθείτε σε μια ονομαστική ομάδα με \k<name> (ή με οποιαδήποτε από τις μορφές αγκίστρων/εισαγωγικών παρακάτω):

/(?<quote>["'])(.*?)\k<quote>/;   # same quote at start and end

Τα ονόματα ακολουθούν τους κανόνες αναγνωριστικών της Perl ([_A-Za-z][_A-Za-z0-9]*)· δεν μπορούν να ξεκινούν με ψηφίο και δεν μπορούν να περιέχουν παύλες.

Αν δύο διακριτές ομάδες μοιράζονται ένα όνομα, το $+{name} αναφέρεται στην πιο αριστερή ορισμένη ομάδα στην αντιστοίχιση. Αυτό σπάνια το θέλετε εκτός μιας επαναφοράς διακλάδωσης ((?|…)), όπου τα κοινά ονόματα είναι ιδιωματικά.

(?P<name>…) — συμβατότητα με Python/PCRE#

Για προγραμματιστές που μεταφέρουν από το άρθρωμα re της Python ή το PCRE, η Perl δέχεται τις γραφές τύπου Python:

Μορφή Python

Ισοδύναμο Perl

(?P<NAME>...)

(?<NAME>...)

(?P=NAME)

\k<NAME>

(?P>NAME)

(?&NAME)

Οι μορφές Python δουλεύουν αλλά δεν είναι ιδιωματική Perl· προτιμήστε τις φυσικές μορφές σε νέο κώδικα.

Οπισθαναφορές#

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

Μορφή

Αναφέρεται σε

\1\9

πρώτη έως ένατη συλλαμβάνουσα ομάδα (παλαιά μορφή)

\g1, \g2

αριθμημένη σύλληψη· ισοδύναμο με \1, \2

\g{1}

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

\g-1

πιο πρόσφατα ανοιχθείσα συλλαμβάνουσα ομάδα (σχετική)

\g{-2}

δεύτερη πιο πρόσφατα ανοιχθείσα ομάδα

\k<name>

ονομαστική σύλληψη

\k'name'

ίδιο, μορφή με απλά εισαγωγικά

\k{name}

ίδιο, μορφή με άγκιστρα (επιτρέπει περιβάλλοντα διαστήματα)

\g{name}

ονομαστική, εναλλακτική γραφή

Παραδείγματα:

# Match a three-letter word followed by a space and the same word.
"the the other day" =~ /\b(\w{3})\s\1\b/;   # $1 eq 'the'

# Match a four-letter, three-letter, two-letter, or one-letter
# run followed by itself.
/^(\w{1,4})\1$/;    # 'beriberi', 'booboo', 'coco', 'mama', 'papa'

\g{…} έναντι \1 για αποσαφήνιση#

Χρησιμοποιείτε \g{…}\k<…>) όταν ψηφία ή χαρακτήρες που μοιάζουν με οκταδικούς ακολουθούν την αναφορά, για να αποφύγετε την ασάφεια:

/(\d)abc\g{1}23/;    # the '1' refers to group 1, '23' is literal
/(\d)abc\123/;       # '\123' is octal 0x53 ('S'), not group 1

Ο κανόνας της Perl για τη γυμνή μορφή \N: τα \1 έως \9 σημαίνουν πάντα οπισθαναφορές. Τα \10, \11, … σημαίνουν οπισθαναφορά μόνο αν έχουν ανοίξει τόσες ομάδες σύλληψης νωρίτερα στο μοτίβο· αλλιώς είναι κυριολεκτικοί οκταδικοί χαρακτήρες. Γι” αυτό το \g{...} είναι η ασφαλέστερη μορφή όταν το μοτίβο χτίζεται με συνένωση.

Αν χρησιμοποιείτε τη μορφή με άγκιστρα, επιτρέπονται προαιρετικά περιβάλλοντα διαστήματα — τα \g{ -1 } και \k{ name } είναι έγκυρα.

Σχετικές οπισθαναφορές#

Τα \g-1, \g{-1} αναφέρονται στην αμέσως προηγούμενη συλλαμβάνουσα ομάδα· το \g{-2} αναφέρεται στην προηγούμενη αυτής· κ.ο.κ. Η απόσταση μετράται με ανοιγμένες παρενθέσεις, συμπεριλαμβανομένων των μη κλειστών. Αυτό έχει σημασία όταν ένα τμήμα μοτίβου παρεμβάλλεται μέσα σε ένα άλλο:

my $pair = '([a-z])(\d)\g{-1}\g{-2}';   # a11a, g22g, x33x, ...

# Embed it: outer group shifts numbering by 1, but relative
# backreferences still work:
"code=e99e" =~ /^(\w+)=$pair$/;   # matches

Χωρίς σχετικές οπισθαναφορές, αυτό θα απαιτούσε να ξέρετε πόσες ομάδες προηγούνται του $pair σε κάθε σημείο παρεμβολής.

Οι ονομαστικές και οι σχετικές αναφορές κάνουν τα μακριά μοτίβα ανθεκτικά στην αντιγραφή-επικόλληση.

Ατομικές ομάδες: (?>…)#

Το (?>…) είναι μια μη συλλαμβάνουσα ομάδα της οποίας τα περιεχόμενα, αφού ταιριάξουν, δεσμεύονται. Η μηχανή δεν μπορεί να οπισθοχωρήσει μέσα στην ομάδα σε αποτυχία — μόνο να την προσπεράσει ως σύνολο.

"aaab" =~ /a*ab/;       # matches: a* gives back one 'a'
"aaab" =~ /(?>a*)ab/;   # does not match: a* takes all, refuses to give

Η πλήρης ανάλυση βρίσκεται στο κεφάλαιο performance. Η κατασκευή τεκμηριώνεται εδώ επειδή η σύνταξή της είναι μια ομάδα σε παρενθέσεις· δομικά ανήκει στο κεφάλαιο των συλλήψεων δίπλα στο (?:…).

Οι κτητικοί ποσοδείκτες (*+, ++, ?+, {n,m}+) είναι ακριβής συντακτική ζάχαρη για (?>…) γύρω από το ποσοδεικτημένο άτομο. Τα ακόλουθα είναι ισοδύναμα:

Κτητικός

Μορφή ατομικής ομάδας

PAT*+

(?>PAT*)

PAT++

(?>PAT+)

PAT?+

(?>PAT?)

PAT{n,m}+

(?>PAT{n,m})

Η γραφή μακράς μορφής (*atomic:…) γίνεται επίσης δεκτή.

Αναδρομικά υπομοτίβα#

Οι regex της Perl μπορούν να αναφέρονται πίσω σε μια ομάδα σύλληψης σαν να ήταν κλήση υπορουτίνας. Η κατασκευή ξανατρέχει το συλληφθέν μοτίβο στην τρέχουσα θέση. Αυτό κάνει αληθινά αναδρομικές δομές αντιστοιχίσιμες — ισοζυγισμένες παρενθέσεις, S-εκφράσεις, εμφωλευμένες αγκύλες — χωρίς να βγαίνετε από τη DSL της regex.

Μορφή

Καλεί αναδρομικά

(?R)

ολόκληρο το μοτίβο

(?0)

ίδιο με (?R)

(?1)

ομάδα 1

(?2)

ομάδα 2 (κ.ο.κ.)

(?-1)

πιο πρόσφατα ανοιχθείσα ομάδα (σχετική)

(?+1)

επόμενη ομάδα προς άνοιγμα (σχετική προς τα εμπρός)

(?&NAME)

ονομαστική ομάδα

(?P>NAME)

ίδιο με (?&NAME) (συμβατό με Python)

Σημείωση: η σχετική αναδρομή μετράει μη κλειστές ομάδες, σε αντίθεση με τις σχετικές οπισθαναφορές. Το (?-1) σημαίνει πάντα την πιο πρόσφατα ανοιχθείσα ομάδα είτε έχει κλείσει είτε όχι.

Ένας αντιστοιχιστής ισοζυγισμένων παρενθέσεων:

my $bal = qr/
    (?(DEFINE)
        (?<paren>
            \(                  # opening paren
            (?:
                [^()]++         # non-paren run, possessive
              | (?&paren)       # or a balanced sub-group, recursively
            )*+
            \)                  # closing paren
        )
    )
    (?&paren)
/x;

"((a)(b(c)))" =~ /^$bal$/;   # matches

Το μπλοκ (?(DEFINE)...) δηλώνει ένα ονομαστικό υπομοτίβο χωρίς το ίδιο να ταιριάζει με τίποτα· το (?&paren) μετά από αυτό καλεί εκείνο το υπομοτίβο, και το αναδρομικό (?&paren) μέσα στο σώμα είναι αυτό που δίνει το απεριόριστο βάθος. Το (?R) δεν θα δούλευε εδώ — καλεί αναδρομικά ολόκληρο το περικλείον μοτίβο, συμπεριλαμβανομένων των αγκύρων ^ και $ που προστίθενται στο σημείο κλήσης, κάτι που αναγκάζει κάθε επίπεδο αναδρομής να απαιτήσει αρχή και τέλος συμβολοσειράς.

Ένα πιο λεπτομερές αναδρομικό μοτίβο: ταίριαξε μια συνάρτηση foo(...) όπου το όρισμα μπορεί το ίδιο να περιέχει ισοζυγισμένες παρενθέσεις.

my $re = qr/(            # group 1: full function call
              foo
              (          # group 2: parens with content
                \(
                  (      # group 3: contents of parens
                    (?:
                       (?> [^()]+ )   # non-paren without backtracking
                     |
                       (?2)           # recurse to group 2
                    )*
                  )
                \)
              )
            )/x;

'foo(bar(baz)+baz(bop))' =~ /$re/ and
    print "1: $1\n2: $2\n3: $3\n";
# 1: foo(bar(baz)+baz(bop))
# 2: (bar(baz)+baz(bop))
# 3: bar(baz)+baz(bop)

Κατάσταση συλλήψεων μέσα σε αναδρομή#

Όταν μια ομάδα καλεί αναδρομικά τον εαυτό της, οι συλλήψεις που ορίζονται μέσα στην αναδρομή δεν είναι ορατές στον καλούντα αφού επιστρέψει η αναδρομή. Η αναδρομική κλήση έχει τη δική της κατάσταση συλλήψεων, η οποία απορρίπτεται κατά την επιστροφή. Γι” αυτό τα περισσότερα αναδρομικά μοτίβα τυλίγουν μια δευτερεύουσα ομάδα σύλληψης γύρω από την αναδρομική κλήση όταν χρειάζεται το ταιριασμένο κείμενο:

/(?<NAME>(?&NAME_PAT))(?<ADDR>(?&ADDRESS_PAT))
 (?(DEFINE)
   (?<NAME_PAT>....)
   (?<ADDRESS_PAT>....)
 )/x

Εδώ το $+{NAME} είναι η εξωτερική σύλληψη· το $+{NAME_PAT} είναι απροσδιόριστο επειδή ζούσε μόνο μέσα στην αναδρομή.

Το ανώτατο όριο βάθους αναδρομής είναι μεταγλωττισμένο στη μηχανή. Μοτίβα που καλούν αναδρομικά χωρίς να καταναλώνουν είσοδο αποτυγχάνουν άμεσα αντί να τρέχουν επ” άπειρον — η μηχανή ανιχνεύει τον κύκλο.

Μπλοκ (DEFINE)#

Το μπλοκ (?(DEFINE)…) περιέχει ονομαστικά υπομοτίβα που ποτέ δεν τρέχουν από μόνα τους — καλούνται μόνο μέσω (?&NAME). Έτσι γράφεται μια regex που μοιάζει με μια μικρή γραμματική:

my $email = qr/
    \A (?&LOCAL) @ (?&DOMAIN) \z
    (?(DEFINE)
        (?<LOCAL>   [\w.+-]+ )
        (?<DOMAIN>  (?&LABEL) (?: \. (?&LABEL) )+ )
        (?<LABEL>   [a-zA-Z0-9] (?: [a-zA-Z0-9-]* [a-zA-Z0-9] )? )
    )
/x;

Το μπλοκ DEFINE στο τέλος κρατά τα ονομαστικά υπομοτίβα σε ένα μέρος· η κορυφή του μοτίβου διαβάζεται ως καθαρή προδιαγραφή.

Δύο προειδοποιήσεις:

  • Τα υπομοτίβα σε ένα μπλοκ DEFINE προσμετρώνται στην απόλυτη και σχετική αρίθμηση των ομάδων σύλληψης στο περιβάλλον μοτίβο. Ονομάστε τα πάντα στο DEFINE ώστε να μην χρειάζεται να μετράτε.

  • Ο βελτιστοποιητής είναι λιγότερο αποτελεσματικός σε μοτίβα τύπου DEFINE από ό,τι στην ισοδύναμη ενσωματωμένη μορφή. Τρέχουν, αλλά όχι πάντοτε σε μέγιστη ταχύτητα.

Υπό συνθήκη μοτίβα: (?(cond)yes|no)#

Ένα υπό συνθήκη μοτίβο επιλέγει ανάμεσα σε δύο υπομοτίβα βάσει ενός ελέγχου σε χρόνο εκτέλεσης:

  • (?(N)YES|NO) — ταίριαξε YES αν η ομάδα N ταίριαξε κάτι, αλλιώς NO.

  • (?(<NAME>)YES|NO) — ίδιο, αλλά με όνομα.

  • (?(?=LOOK)YES|NO) — ταίριαξε YES αν η πρόσθια διεκδίκηση επιτύχει.

  • (?(?{CODE})YES|NO) — ταίριαξε YES αν το μπλοκ κώδικα επιστρέψει αληθές.

  • (?(R)YES|NO) — ταίριαξε YES αν βρίσκεστε αυτή τη στιγμή μέσα σε αναδρομή.

  • (?(R1)YES|NO) — ταίριαξε YES αν γίνεται αναδρομή μέσω της ομάδας 1.

  • (?(R&NAME)YES|NO) — ταίριαξε YES αν γίνεται αναδρομή μέσω ονομαστικής ομάδας.

Η διακλάδωση NO είναι προαιρετική· η απουσία NO αντιμετωπίζεται ως «ταίριαξε πάντα».

Επεξεργασμένο παράδειγμα: προαιρετικά παρενθεσιακό κείμενο. Αν η είσοδος ανοίγει με (, απαίτησε ένα τελικό ). Διαφορετικά μην επιτρέπεις παρενθέσεις:

m{ ( \( )?            # optional opening paren, group 1
   [^()]+             # body (no parens)
   (?(1) \) )         # if group 1 matched, require closing paren
}x;

Χωρίς υπό συνθήκη μοτίβα, αυτό θα χρειαζόταν μια εναλλαγή που καλύπτει και τα δύο σχήματα· η υπό συνθήκη εκδοχή φανερώνει την πρόθεση.

Επεξεργασμένο παράδειγμα: ισοζυγισμένη γραμματική με ονομαστική αναδρομή. Χρήσιμο μέσα σε μπλοκ DEFINE για επικύρωση ευαίσθητη ως προς τα συμφραζόμενα:

qr/
    (?<expr>
        (?<atom>  [a-z]+ | \( (?&expr) \) )
        (?: \s* [+*] \s* (?&atom) )*
    )
/x

Τα υπό συνθήκη είναι το τμήμα της regex που μοιάζει περισσότερο με πραγματική γλώσσα προγραμματισμού· καταφύγετε σε αυτά όταν μια εναλλαγή αμοιβαία αποκλειόμενων σχημάτων θα ήταν δυσκίνητη.

Επαναφορά διακλάδωσης: (?|…)#

Μέσα στο (?|…), κάθε εναλλακτική διακλάδωση ξεκινά την αρίθμηση των συλλήψεών της από την ίδια θέση. Μετά την ομάδα, η αρίθμηση συνεχίζει μία θέση μετά το μέγιστο από όλες τις διακλαδώσεις. Η πλήρης ανάλυση βρίσκεται στο κεφάλαιο alternation· οι συνέπειες ως προς την αρίθμηση συλλήψεων ανήκουν εδώ.

# Before  ---------------branch-reset----------- after
/ ( a )  (?| x ( y ) z | (p (q) r) | (t) u (v) ) ( z ) /x
# 1            2         2  3        2     3     4

Αφού ταιριάξει αυτό το μοτίβο:

  • Το $1 είναι πάντα το αρχικό a.

  • Το $2 είναι y, (p q r), ή t ανάλογα με τη διακλάδωση.

  • Το $3 είναι undef από τη διακλάδωση 1, q από τη διακλάδωση 2, v από τη διακλάδωση 3.

  • Το $4 είναι το τελικό z.

Αν χρησιμοποιείτε ονομαστικές συλλήψεις μέσα σε (?|…), χρησιμοποιείτε τα ίδια ονόματα με την ίδια σειρά σε κάθε διακλάδωση:

/(?|  (?<a> x ) (?<b> y )
   |  (?<a> z ) (?<b> w )) /x;

Η ανάμειξη ονομάτων μεταξύ διακλαδώσεων λειτουργεί (η Perl αναλύει στο πιο αριστερό ορισμένο όνομα) αλλά παράγει εκπληκτικά αποτελέσματα: κάθε όνομα αναφέρεται στην ίδια θέση, οπότε τα $+{a} και $+{b} μπορεί να έχουν την ίδια τιμή σε διαφορετικές διακλαδώσεις.

Πίνακες θέσεων: @- και @+#

Μετά από μια επιτυχημένη αντιστοίχιση, οι @- και @+ περιέχουν τις μετατοπίσεις αρχής και τέλους ολόκληρης της αντιστοιχίας και κάθε ομάδας σύλληψης:

  • $-[0], $+[0] — μετατοπίσεις ολόκληρης της αντιστοιχίας.

  • $-[n], $+[n] — μετατοπίσεις της n-οστής σύλληψης, ή undef αν η ομάδα δεν συμμετείχε.

my $s = "Mmm...donut, thought Homer";
if ($s =~ /^(Mmm|Yech)\.\.\.(donut|peas)/) {
    for my $i (1 .. $#-) {
        printf "Match %d: %s at (%d,%d)\n",
               $i,
               substr($s, $-[$i], $+[$i] - $-[$i]),
               $-[$i], $+[$i];
    }
}
# Match 1: Mmm at (0,3)
# Match 2: donut at (6,11)

Οι μετατοπίσεις είναι συχνά ευκολότερες από τις υποσυμβολοσειρές όταν χρειάζεται να τροποποιήσετε την αρχική συμβολοσειρά στη θέση αντιστοίχισης.

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

Η Perl εκθέτει όλες τις αριθμημένες συλλήψεις ως έναν ενιαίο πίνακα @{^CAPTURE}, ευρετηριασμένο από το 0 (όπου ο δείκτης 0 είναι το $1, ο δείκτης 1 είναι το $2, …):

$string =~ /$pattern/ and my @captured = @{^CAPTURE};

Αυτό είναι βολικό όταν το πλήθος των συλλήψεων είναι μεταβλητό ή άγνωστο — κώδικας που παίρνει συλλήψεις από μοτίβο παρεχόμενο από τον χρήστη δεν χρειάζεται πια να μετράει ( για να ξέρει πόσα $1/$2/… να διαβάσει.

Η ευρετηρίαση απαιτεί την οριοθετημένη μορφή με άγκιστρα:

print "${^CAPTURE[0]}";    # equivalent to $1

Τα %{^CAPTURE_ALL} και %{^CAPTURE_NAMES} υπάρχουν για ενδοσκόπηση ονομαστικών συλλήψεων — δείτε perlvar.

Προ-αντιστοιχία, αντιστοιχία, μετα-αντιστοιχία#

Η Perl ορίζει τρία ειδικά βαθμωτά μετά από κάθε αντιστοίχιση που εκθέτουν το περιβάλλον κείμενο:

  • $` — οτιδήποτε πριν την αντιστοιχία (η προ-αντιστοιχία).

  • $& — η ίδια η αντιστοιχία.

  • $' — οτιδήποτε μετά την αντιστοιχία (η μετα-αντιστοιχία).

"the cat caught the mouse" =~ /cat/;
# $`   = 'the '
# $&   = 'cat'
# $'   = ' caught the mouse'

Οι ονομαστικές παραλλαγές ${^PREMATCH}, ${^MATCH}, ${^POSTMATCH} είναι ισοδύναμες. Και οι δύο μορφές είναι μηδενικού κόστους· χρησιμοποιήστε όποια διαβάζεται καλύτερα.

$+ και $^N#

Το $+ περιέχει την αντιστοιχία της ομάδας σύλληψης με τον υψηλότερο αριθμό που πέτυχε.

Το $^N περιέχει την αντιστοιχία της πιο πρόσφατα κλειστής ομάδας σύλληψης — της πιο δεξιάς ) που ολοκληρώθηκε. Αυτό είναι ακριβώς αυτό που θέλετε μέσα σε ένα μπλοκ κώδικα (?{…}) για να προσπελάσετε την πιο πρόσφατη σύλληψη χωρίς να μετράτε παρενθέσεις:

$_ = "The brown fox jumps over the lazy dog";
/the (\S+)(?{ $color = $^N }) (\S+)(?{ $animal = $^N })/i;
print "color = $color, animal = $animal\n";
# color = brown, animal = fox

Δείτε το κεφάλαιο performance για την πλήρη ιστορία ενσωματωμένου κώδικα.

Μεγιστοποίηση υπο-εκφράσεων POSIX — τι κάνουν άλλες μηχανές#

Μερικές μηχανές (οποιοδήποτε συμμορφούμενο POSIX NFA, συν υλοποιήσεις μηχανών σχεδιασμένες να μιμούνται το POSIX, συν τα περισσότερα υβρίδια DFA) ακολουθούν διαφορετικό κανόνα για το ποια αντιστοιχία κερδίζει όταν είναι δυνατές πολλές: πιο μακρά-πιο αριστερή, με τον περιορισμό ότι κάθε υπο-έκφραση συλλαμβάνει τη μακρύτερη υποσυμβολοσειρά που μπορεί.

Για το μοτίβο (to|top)(o|polo)?(gical|o?logical) εφαρμοσμένο στο topological:

  • Η Perl (και η PCRE2, και κάθε παραδοσιακό NFA) δοκιμάζει τις εναλλακτικές από αριστερά προς τα δεξιά. Το (to|top) ταιριάζει με to· η μηχανή προχωράει. Το (o|polo) ταιριάζει με o· προχωράει. Το (gical|o?logical) ταιριάζει με logical. Τέλος.

  • Η σημασιολογία POSIX απαιτεί η συνολική αντιστοιχία να είναι η μακρύτερη, και ανάμεσα σε αντιστοιχίες ίσου μήκους, η σύλληψη κάθε επιμέρους ομάδας να είναι η μακρύτερη. Το αποτέλεσμα είναι top polo gical, όπου κάθε σύλληψη είναι στο πλατύτερό της.

Οι σύγχρονες μηχανές ουσιαστικά ποτέ δεν χρησιμοποιούν σημασιολογία POSIX-NFA, αλλά μερικά εργαλεία βασισμένα σε DFA (grep, awk) την προσεγγίζουν. Το κεφάλαιο cross-engine έχει τον πλήρη πίνακα.

Μεταξύ μηχανών: συλλήψεις και οπισθαναφορές#

Αυτό είναι το χαρακτηριστικό όπου οι μηχανές αποκλίνουν περισσότερο. Ο πλήρης πίνακας βρίσκεται στο κεφάλαιο cross-engine· οι σχετικές γραμμές αποσπασμένες:

Χαρακτηριστικό

Perl 5.42

PCRE2

Emacs

POSIX BRE

POSIX ERE

RE2/Go

Αριθμημένες συλλήψεις

ναι

ναι

ναι

ναι

αυστηρά όχι, GNU ναι

ναι (χωρίς οπισθαναφορές)

Οπισθαναφορές σε μοτίβο (\1–)

ναι

ναι

ναι

ναι (\1\9)

αυστηρά όχι

ΟΧΙ

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

ναι

ναι

ΟΧΙ

ΟΧΙ

ΟΧΙ

ναι

Επαναφορά διακλάδωσης (?|…)

ναι

ναι

ΟΧΙ

ΟΧΙ

ΟΧΙ

ΟΧΙ

πίνακας @{^CAPTURE}

ναι

ΟΧΙ

ΟΧΙ

ΟΧΙ

ΟΧΙ

ΟΧΙ (διαφορετικό API)

Αναδρομικά υπομοτίβα (?R) κτλ.

ναι

ναι

ΟΧΙ

ΟΧΙ

ΟΧΙ

ΟΧΙ

Υπό συνθήκη μοτίβα (?(c)y|n)

ναι

ναι

ΟΧΙ

ΟΧΙ

ΟΧΙ

ΟΧΙ

Το πιο σημαντικό συμπέρασμα: η regex της RE2 / Go δεν υποστηρίζει καθόλου οπισθαναφορές. Αυτό δεν είναι παράβλεψη· είναι το τίμημα που πληρώνει η RE2 για εγγυημένη αντιστοίχιση γραμμικού χρόνου. Μοτίβα που χρειάζονται οπισθαναφορές δεν είναι κανονικές γλώσσες και δεν μπορούν να αντιστοιχιστούν από ένα DFA σε γραμμικό χρόνο.

Αν μεταφέρετε regex της Perl σε υπηρεσία Go, ελέγξτε τις για οπισθαναφορές προτού υποθέσετε ότι η μετατροπή είναι μηχανική. Μοτίβα όπως /(?<a>\w+) and \k<a>/ απλώς δεν μπορούν να εκφραστούν στην RE2.

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

  • Το κεφάλαιο alternation — επαναφορά διακλάδωσης και οι συνέπειες αρίθμησης εναλλακτικών του (?|…).

  • Το κεφάλαιο performance — ατομικές ομάδες, αναδρομικά μοτίβα σε επεξεργασμένα παραδείγματα, ενσωματωμένος κώδικας.

  • Το κεφάλαιο modifiers/n για καταστολή όλων των συλλήψεων.

  • perlvar%+, %-, $&, $^N, @{^CAPTURE} και συναφή.