Βασικά#
Η μικρότερη χρήσιμη 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// είναι ένα από τα τέσσερα:
Τελεστής | Σκοπός |
|---|---|
| αντιστοίχιση — ταιριάζει το μοτίβο με τη συμβολοσειρά; |
| αντικατάσταση — αντικαθιστά την αντιστοιχία με κάτι άλλο |
| μεταγλωττίζει ένα αντικείμενο μοτίβου για επαναχρησιμοποίηση |
| μετάφραση χαρακτήρα προς χαρακτήρα |
Το 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 χωρίς να επηρεάζει άλλον κώδικα. Δεν είναι προεπιλεγμένη γιατί θα έσπαζε λειτουργικά παλαιότερα μοτίβα· ο νέος κώδικας θα έπρεπε να την εξετάζει.
Ακολουθίες διαφυγής#
Οι μη εκτυπώσιμοι χαρακτήρες χρησιμοποιούν τις ίδιες ακολουθίες διαφυγής όπως στις συμβολοσειρές διπλών εισαγωγικών:
Ακολουθία | Ταιριάζει με |
|---|---|
| στηλοθέτη (tab) |
| νέα γραμμή |
| επαναφορά κεφαλής (carriage return) |
| αλλαγή σελίδας (form feed) |
| ειδοποίηση (κουδούνι) |
| escape ( |
| byte NUL |
| byte με δεκαεξαδική τιμή HH |
| σημείο κώδικα Unicode με δεκαεξαδική τιμή |
| οκταδικό σημείο κώδικα |
| control-X |
| χαρακτήρας 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 σε τέσσερις φάσεις. Η γνώση των φάσεων εξηγεί μερικά ερωτήματα του τύπου «γιατί δουλεύει αυτό;»:
Φάση Α: ο αναλυτής αναγνωρίζει το διαχωριστικό και εντοπίζει το τέλος του μοτίβου. Τα σχόλια
(?#…)αφαιρούνται.Φάση Β: το μοτίβο αναλύεται ως συμβολοσειρά τύπου διπλών εισαγωγικών — οι μεταβλητές παρεμβάλλονται, οι ακολουθίες διαφυγής επεξεργάζονται, το
\Q…\Eμεταφράζεται σε διαφυγή τύπουquotemeta.Φάση Γ: υπό
/xή/xx, οι λευκοί χαρακτήρες χωρίς διαφυγή και τα σχόλια μετά το#αφαιρούνται.Φάση Δ: ο μεταγλωττιστής 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 — τι μπορεί να πάει στραβά.