Βασικά#

Η μικρότερη χρήσιμη regex είναι μια απλή συμβολοσειρά. Το "Hello World" =~ /World/ ρωτά: περιέχει η συμβολοσειρά στα αριστερά το μοτίβο στα δεξιά; Το περιέχει, άρα η έκφραση είναι αληθής.

if ("Hello World" =~ /World/) {
    print "matched\n";
}

Τα // περικλείουν το μοτίβο. Ο τελεστής =~ συνδέει το μοτίβο με τη συμβολοσειρά που θέλετε να ελέγξετε. Χωρίς τελεστή σύνδεσης, η Perl εφαρμόζει το μοτίβο στο $_.

Ο τελεστής αντιστοίχισης#

Η εκτεταμένη μορφή είναι m//:

"Hello World" =~ m/World/;
"Hello World" =~ m!World!;    # alternate delimiters
"Hello World" =~ m{World};    # paired delimiters

Το m σάς επιτρέπει να επιλέξετε οποιοδήποτε διαχωριστικό. Αυτό έχει σημασία όταν το ίδιο το μοτίβο περιέχει το προεπιλεγμένο διαχωριστικό / — συγκρίνετε

"/usr/bin/perl" =~ /\/usr\/bin\/perl/;  # leaning toothpick syndrome
"/usr/bin/perl" =~ m!/usr/bin/perl!;    # clearer

Τα ζευγαρωτά διαχωριστικά ({}, (), [], <>) εμφωλεύονται, κάτι που είναι χρήσιμο όταν το μοτίβο περιέχει τον χαρακτήρα του διαχωριστικού.

Χωρίς m, η αρχική κάθετος είναι υποχρεωτική: μόνο /pat/. Με m, το αρχικό m είναι υποχρεωτικό: m{pat}, όχι {pat}.

Μια μικρή αλλά χρήσιμη γωνία: τα m'' (απλά εισαγωγικά ως διαχωριστικά) κάνουν το μοτίβο τύπου απλών εισαγωγικών — χωρίς παρεμβολή μεταβλητών, χωρίς διαφυγές διπλών εισαγωγικών. Χρήσιμο όταν το μοτίβο πρόκειται να περιέχει κυριολεκτικό $ ή @ και δεν θέλετε να τα διαφεύγετε.

'price: $10' =~ m'\$\d+';      # $-as-anchor would be /$\d+/
'price: $10' =~ m'$10';        # literal '$10' — no interpolation

Άλλοι τελεστές regex#

Το m// είναι ένα από τα τέσσερα:

Τελεστής

Σκοπός

m//

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

s///

αντικατάσταση — αντικαθιστά την αντιστοιχία με κάτι άλλο

qr//

μεταγλωττίζει ένα αντικείμενο μοτίβου για επαναχρησιμοποίηση

tr/// (επίσης y///)

μετάφραση χαρακτήρα προς χαρακτήρα

Το tr/// δεν είναι τελεστής regex παρότι βρίσκεται στην ίδια γειτονιά — εκτελεί μετάφραση κλάσεων χαρακτήρων, όχι αντιστοίχιση μοτίβων. Αναφέρεται για πληρότητα· δείτε tr για τη σημασιολογία του.

Τα m, s και qr μοιράζονται όλα τη σύνταξη regex που καλύπτεται σε αυτόν τον οδηγό. Οι διαφορές βρίσκονται σε αυτό που κάνουν με μια επιτυχημένη αντιστοιχία.

Επαναχρησιμοποίηση μοτίβου με qr//#

Το qr// μεταγλωττίζει ένα μοτίβο μία φορά και παράγει ένα επαναχρησιμοποιήσιμο αντικείμενο. Χρησιμοποιήστε το όταν το ίδιο μοτίβο αντιστοιχίζεται επανειλημμένα, ιδίως μέσα σε βρόχους:

my $word = qr/\b[a-z]+\b/;

for my $line (@lines) {
    while ($line =~ /$word/g) {
        print "$&\n";
    }
}

Το μεταγλωττισμένο αντικείμενο qr// εισάγεται σε άλλα μοτίβα μέσω παρεμβολής. Επίσης σας επιτρέπει να χτίζετε μοτίβα από ονομασμένα τμήματα, κάτι που γίνεται απαραίτητο για κάθε regex πάνω από περίπου δέκα γραμμές:

my $name   = qr/[A-Z][a-z]+/;
my $number = qr/\d+/;
my $entry  = qr/$name \s+ $number/x;

"Alice 42" =~ /^$entry$/;   # matches

Το ίδιο όφελος της μίας μεταγλώττισης, εκφρασμένο στο σωστό επίπεδο αφαίρεσης.

Σύνδεση: =~ και !~#

Το =~ ρωτά «ταιριάζει;». Το !~ ρωτά «αποτυγχάνει να ταιριάξει;».

$s = "Hello World";

print "yes\n" if $s =~ /World/;   # yes
print "no\n"  if $s !~ /planet/;  # no

Το !~ δεν είναι ξεχωριστή κατασκευή regex — είναι η αρνημένη σύνδεση. Ισοδυναμεί με not ($s =~ /pat/).

Αντιστοίχιση με το $_#

Αν παραλείψετε τη σύνδεση, η αντιστοίχιση γίνεται με το $_:

for ("cat", "dog", "bird") {
    print "has an 'o'\n" if /o/;   # implicit: $_ =~ /o/
}

Αυτό είναι ιδιωματικό σε βρόχους while (<>), μέσα σε grep και map, και μέσα σε βρόχους for που ορίζουν το $_.

Διάκριση πεζών-κεφαλαίων και η προεπιλεγμένη άγκυρα#

Οι αντιστοιχίες γίνονται με διάκριση πεζών-κεφαλαίων και χωρίς αγκύρωση:

"Hello" =~ /hello/;    # does not match — case differs
"Hello" =~ /ell/;      # matches — inside the string is fine

Για αντιστοίχιση χωρίς διάκριση πεζών-κεφαλαίων, προσθέστε /i. Για να περιορίσετε την αντιστοιχία στην αρχή ή το τέλος της συμβολοσειράς, χρησιμοποιήστε άγκυρες. Και τα δύο καλύπτονται στα δικά τους κεφάλαια.

Όταν ένα μοτίβο μπορεί να ταιριάξει σε περισσότερες από μία θέσεις, η Perl δοκιμάζει από τα αριστερά και παίρνει την πρώτη που λειτουργεί:

"That hat is red" =~ /hat/;   # matches 'hat' in 'That', not in 'hat'

Ο κανόνας «η αντιστοιχία πιο αριστερά κερδίζει» είναι θεμελιώδης και υπερισχύει κάθε άλλης προτίμησης αντιστοίχισης: μια αντιστοιχία σε προγενέστερη θέση είναι πάντα καλύτερη από μια αντιστοιχία σε μεταγενέστερη θέση, ανεξάρτητα από τις εσωτερικές επιλογές κάθε αντιστοίχισης. Αυτό αποκαλείται μερικές φορές ιδιότητα bump-along: η μηχανή προσπαθεί να ταιριάξει στη θέση 0· αν αποτύχει, μετακινείται στη θέση 1· αν αποτύχει, στη θέση 2· κ.ο.κ., επιστρέφοντας την πρώτη επιτυχία.

Μετα-χαρακτήρες#

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

{ } [ ] ( ) ^ $ . | * + ? \

Δύο ακόμα είναι ειδικοί μόνο σε συγκεκριμένα περιβάλλοντα:

  • Το - είναι μετα-χαρακτήρας μόνο μέσα σε κλάση χαρακτήρων, όπου σχηματίζει εύρος ([a-z]). Εκτός […] είναι κυριολεκτικό.

  • Το # είναι μετα-χαρακτήρας μόνο υπό τη σημαία /x, όπου εισάγει σχόλιο μέχρι το τέλος της γραμμής. Χωρίς /x είναι κυριολεκτικό.

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

"2+2=4" =~ /2+2/;    # fails — '+' is a quantifier, needs escaping
"2+2=4" =~ /2\+2/;   # matches

"end." =~ /end\./;   # matches a literal dot
"end." =~ /end./;    # also matches — but . matches any character,
                     # so this would also match "endx", "end ", etc.

Η ίδια η ανάστροφη κάθετος είναι μετα-χαρακτήρας, οπότε μια κυριολεκτική ανάστροφη κάθετος σε ένα μοτίβο χρειάζεται \\:

'C:\WIN32' =~ /C:\\WIN/;    # matches

Ένας μετα-χαρακτήρας που δεν έχει τίποτα ειδικό να κάνει στο περιβάλλον του επιστρέφει στο να ταιριάζει με τον εαυτό του. Το } κλείνει μόνο έναν ποσοδείκτη {…}· εκτός αυτού του περιβάλλοντος είναι κυριολεκτικό }. Αυτό είναι βολικό αλλά εύκολο να παρερμηνευτεί· δείτε Αυστηρή λειτουργία παρακάτω.

Αυστηρή λειτουργία: use re 'strict'#

Το use re 'strict' μετατρέπει την προηγουμένως ανεκτή χαλαρότητα στις regex σε σφάλματα κατά τη μεταγλώττιση. Χρησιμοποιήστε το όταν θέλετε ο μεταγλωττιστής regex να επισημαίνει μοτίβα που πιθανώς σημαίνουν κάτι διαφορετικό από αυτό που γράψατε:

use re 'strict';

/abc{,1/;        # error: unescaped '{' in non-quantifier context
/(?-p)/;         # error: useless negation of always-on flag
/[a-]/;          # error: dash at end of class

Η αυστηρή λειτουργία είναι ανά λεξική εμβέλεια, οπότε μπορεί να ενεργοποιηθεί για αρθρώματα με πολλές regex χωρίς να επηρεάζει άλλον κώδικα. Δεν είναι προεπιλεγμένη γιατί θα έσπαζε λειτουργικά παλαιότερα μοτίβα· ο νέος κώδικας θα έπρεπε να την εξετάζει.

Ακολουθίες διαφυγής#

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

Ακολουθία

Ταιριάζει με

\t

στηλοθέτη (tab)

\n

νέα γραμμή

\r

επαναφορά κεφαλής (carriage return)

\f

αλλαγή σελίδας (form feed)

\a

ειδοποίηση (κουδούνι)

\e

escape (\x1B)

\0

byte NUL

\xHH

byte με δεκαεξαδική τιμή HH

\x{…}

σημείο κώδικα Unicode με δεκαεξαδική τιμή

\o{…}

οκταδικό σημείο κώδικα

\cX

control-X

\N{…}

χαρακτήρας Unicode με όνομα

"1000\t2000" =~ /0\t2/;      # matches
"a\x{263a}b" =~ /\x{263a}/;  # matches U+263A, WHITE SMILING FACE

Η πλήρης ιστορία Unicode βρίσκεται στο κεφάλαιο unicode.

Μεταβλητές μέσα στα μοτίβα#

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

my $word = "house";
"housecat" =~ /$word/;       # matches
"housecat" =~ /${word}cat/;  # matches — braces disambiguate

Για να ταιριάξετε ένα κυριολεκτικό $ ή @, διαφεύγετέ το:

'price: $10' =~ /\$10/;      # matches a literal dollar sign

Αν μια συμβολοσειρά που παρέχεται από τον χρήστη πρόκειται να παρεμβληθεί σε ένα μοτίβο και θέλετε οι μετα-χαρακτήρες της να αντιμετωπιστούν κυριολεκτικά, χρησιμοποιήστε quotemeta — ή το εντός μοτίβου ισοδύναμό του \Q…\E:

my $input = "1+1";
"1+1=2" =~ /\Q$input\E/;     # matches the literal string

Χωρίς \Q…\E το + θα διαβαζόταν ως ποσοδείκτης.

Πώς διαβάζεται μια regex#

Η Perl διαβάζει ένα μοτίβο regex σε τέσσερις φάσεις. Η γνώση των φάσεων εξηγεί μερικά ερωτήματα του τύπου «γιατί δουλεύει αυτό;»:

  1. Φάση Α: ο αναλυτής αναγνωρίζει το διαχωριστικό και εντοπίζει το τέλος του μοτίβου. Τα σχόλια (?#…) αφαιρούνται.

  2. Φάση Β: το μοτίβο αναλύεται ως συμβολοσειρά τύπου διπλών εισαγωγικών — οι μεταβλητές παρεμβάλλονται, οι ακολουθίες διαφυγής επεξεργάζονται, το \Q…\E μεταφράζεται σε διαφυγή τύπου quotemeta.

  3. Φάση Γ: υπό /x ή /xx, οι λευκοί χαρακτήρες χωρίς διαφυγή και τα σχόλια μετά το # αφαιρούνται.

  4. Φάση Δ: ο μεταγλωττιστής regex διαβάζει το αποτέλεσμα και το μετατρέπει στην εσωτερική μορφή της μηχανής.

Η σειρά έχει σημασία επειδή οι φάσεις Β και Δ λειτουργούν σε διαφορετικές αναπαραστάσεις. Το \Q$dir\E επεξεργάζεται στη Φάση Β, πριν το δει ο μεταγλωττιστής regex — μέχρι τη Φάση Δ, τα περιεχόμενα της μεταβλητής έχουν ήδη διαφύγει, και ο μεταγλωττιστής regex βλέπει ένα κυριολεκτικό μοτίβο. Αντιθέτως, το \U…\E ερμηνεύεται από τη Φάση Β ως οδηγία επεξεργασίας συμβολοσειράς (μετατροπή των περιεχομένων σε κεφαλαία), κάτι που σχεδόν σίγουρα δεν είναι αυτό που θέλετε μέσα σε μια regex. Η σύμβαση είναι: \Q και \E για σκοπούς regex· \U, \L, \u, \l μόνο όταν ξέρετε τι κάνετε.

Το (?#comment) αφαιρείται προτού η Φάση Β δει καν το μοτίβο. Ένα κυριολεκτικό # μέσα στο (?#…) είναι μια χαρά. Ένα κυριολεκτικό ) όχι — το σχόλιο τελειώνει στην πρώτη ).

Η νωρίτερη αντιστοιχία κερδίζει — το bump-along#

Ο Friedl το διατυπώνει με ακρίβεια:

Η αντιστοιχία που ξεκινά νωρίτερα κερδίζει.

Η θέση 0 δοκιμάζεται πρώτη, μετά η 1, μετά η 2, κ.ο.κ. Η μηχανή ποτέ δεν προτιμά μια μεταγενέστερη, μακρύτερη ή «πιο αισθητικά καλή» αντιστοιχία έναντι μιας προγενέστερης. Αυτό είναι τόσο θεμελιώδες ώστε διαμορφώνει κάθε άλλον κανόνα σε αυτόν τον οδηγό:

  • «Οι άπληστοι ποσοδείκτες αρπάζουν όσο το δυνατόν περισσότερα» — στην τρέχουσα θέση εκκίνησης. Η αρπαγή συμβαίνει αφού το bump-along έχει επιλέξει τη θέση.

  • «Η πιο αριστερή εναλλακτική κερδίζει» — στην τρέχουσα θέση εκκίνησης. Η εναλλακτική που επιλέγεται επηρεάζει τι συλλαμβάνεται αλλά όχι το πού ξεκινά η αντιστοιχία.

  • Άγκυρες όπως η ^ περιορίζουν ποιες θέσεις είναι νόμιμες, όχι τι προτιμάται ανάμεσα στις νόμιμες.

Πείτε αυτό που εννοείτε#

Το επαναλαμβανόμενο σημείο του Friedl: η ασάφεια στη regex προκαλεί τόσο προβλήματα ορθότητας όσο και προβλήματα απόδοσης. Το παράδειγμα στο οποίο επιμένει:

/-?[0-9]*\.?[0-9]*/

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

Η διόρθωση είναι να απαιτήσετε τουλάχιστον ένα ψηφίο σε τουλάχιστον μία πλευρά της υποδιαστολής:

/-?[0-9]+(\.[0-9]*)?|-?\.[0-9]+/

Δύο εναλλακτικές, κάθε μία απαιτεί τουλάχιστον ένα ψηφίο. Το μοτίβο τώρα ταιριάζει με αυτό που σκόπευε ο συγγραφέας του.

Το μάθημα γενικεύεται. Ένα μοτίβο που μπορεί να ταιριάξει με την κενή συμβολοσειρά συνήθως θα ταιριάξει με την κενή συμβολοσειρά κάπου. Αν η αντιστοιχία σας πρέπει να σημαίνει κάτι, γράψτε απαιτήσεις που την αναγκάζουν να σημαίνει κάτι.

Αντικατάσταση με μια ματιά#

Η αντικατάσταση κειμένου χρησιμοποιεί τον τελεστή s///, ο οποίος δέχεται ένα μοτίβο και μια συμβολοσειρά αντικατάστασης:

my $x = "feed the cat";
$x =~ s/cat/dog/;            # $x is now "feed the dog"

Η αντικατάσταση καλύπτεται σε βάθος στο δικό της κεφάλαιο· αναφέρεται εδώ ώστε να μπορείτε να τη συνδυάσετε με τα παραπάνω δεδομένα. Σχεδόν ό,τι ισχύει για τα μοτίβα m// ισχύει και μέσα στα μοτίβα s///.

Πού να πάτε στη συνέχεια#

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

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

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

  • m — πλήρης αναφορά για τον τελεστή αντιστοίχισης.

  • s — πλήρης αναφορά για την αντικατάσταση.

  • qr — μεταγλώττιση αντικειμένου μοτίβου.

  • quotemeta — διαφυγή συμβολοσειράς για ασφαλή παρεμβολή σε μοτίβο.

  • Το κεφάλαιο character classes — τι ακολουθεί μετά την κυριολεκτική αντιστοίχιση.

  • Το κεφάλαιο performance — τι μπορεί να πάει στραβά.