Μονογραμμικά οδηγούμενα από regex#
Το προηγούμενο κεφάλαιο κράτησε τα regexps στο επίπεδο που γνωρίζουν ήδη οι χρήστες sed ή awk: κυριολεκτικά μοτίβα μέσα σε m// και s///. Το παρόν κεφάλαιο καλύπτει μονογραμμικά των οποίων η μορφή εξαρτάται από κάποιο χαρακτηριστικό regex πέρα από την απλή αντιστοίχιση - εξαγωγές, συλλήψεις, διεκδικήσεις γύρω, κώδικα-στην-αντικατάσταση και μη καταστροφικές επανεγγραφές.
Οι αναγνώστες που γνωρίζουν ήδη τις κατασκευές ((?:...), (?<name>...), (?=...), τους τροποποιητές /e και /r) μπορούν να περιδιαβούν το κείμενο για το πλαίσιο γραμμής εντολών. Όσοι ξέρουν τι κάνει το /g αλλά δεν έχουν γράψει ποτέ /e θα ωφεληθούν περισσότερο διαβάζοντας συνεχόμενα.
Για την ίδια τη γλώσσα regex, δείτε τον οδηγό κανονικών εκφράσεων.
Εξαγωγή όλων των αντιστοιχιών#
Το /g σε περιβάλλον λίστας επιστρέφει κάθε αντιστοιχία, όχι μόνο την πρώτη.
# Every integer in the input
pperl -ne 'print "$_\n" for /-?\d+/g' file.txt
# Every IPv4-shaped substring
pperl -lne 'print for /\b(?:\d{1,3}\.){3}\d{1,3}\b/g' access.log
# Every HTTP header value (naive: no folded headers)
pperl -nE 'say $1 if /^[\w-]+:\s*(.+)$/' headers.txt
Το say (ενεργοποιείται από -E) προσαρτά χαρακτήρα αλλαγής γραμμής, εξοικονομώντας ένα "\n" και κρατώντας το πρόγραμμα σύντομο.
Μη άπληστοι ποσοδείκτες#
*?, +?, ??, {n,m}? - ταιριάζουν με όσο λιγότερους χαρακτήρες γίνεται.
# Everything between the first < and the next > (tag-by-tag)
pperl -lne 'print for /<(.+?)>/g' markup.html
# Every quoted string (double quotes, no escape handling)
pperl -lne 'print for /"([^"]*)"/g' config.txt
Η αρνημένη κλάση χαρακτήρων [^"]* είναι συνήθως ταχύτερη και ασφαλέστερη μη άπληστη επιλογή από το .*? όταν γνωρίζετε τι δεν πρέπει να εμφανιστεί μέσα.
Συλλήψεις στην αντικατάσταση#
Η δεξιά πλευρά του s/// μπορεί να αναφέρεται σε συλλήψεις της αριστερής πλευράς.
# Swap every "word1 word2" pair
pperl -pe 's/(\w+)\s+(\w+)/$2 $1/g' file.txt
# Quote every unquoted key in key=value pairs
pperl -pe 's/(\w+)=/$1="/g; s/=([^"\n]+)/"$1"/g' config.txt
# Increase every trailing number by one
pperl -pe 's/(\d+)$/$1 + 1/e' file.txt
/e - η αντικατάσταση είναι κώδικας Perl#
Ο τροποποιητής /e αντιμετωπίζει την αντικατάσταση ως κώδικα· η τιμή επιστροφής του είναι αυτό που τοποθετείται.
# Number every word in the file (global counter)
pperl -pe 's/(\w+)/++$i . ".$1"/ge' file.txt
# Number words per line (counter reset each line)
pperl -pe '$i = 0; s/(\w+)/++$i . ".$1"/ge' file.txt
# Wrap every integer in base 16
pperl -pe 's/\d+/sprintf "0x%x", $&/ge' file.txt
# Convert Celsius-labelled numbers to Fahrenheit
pperl -pe 's/(\d+)C\b/sprintf "%.1fF", $1 * 9 / 5 + 32/ge' weather.txt
Το /ge μαζί: αποτίμηση της δεξιάς πλευράς ως κώδικα, για κάθε αντιστοιχία. Το $& είναι ολόκληρο το κείμενο που ταίριαξε· τα $1, $2, … είναι συλλήψεις.
Το /ee αποτιμά δύο φορές - το πρώτο e παράγει συμβολοσειρά, το δεύτερο αποτιμά αυτή τη συμβολοσειρά. Χρήσιμο όταν η ίδια η συμβολοσειρά αντικατάστασης είναι δυναμικός κώδικας που διαβάζεται από την είσοδο· χρειάζεται σπάνια, συνιστά κίνδυνο συχνά.
/r - μη καταστροφική αντικατάσταση#
Το /r επιστρέφει την τροποποιημένη συμβολοσειρά αντί να ξαναγράψει το $_. Το πρωτότυπο παραμένει ανέπαφο.
# Print filename and its .bak variant side by side
ls *.txt | pperl -pe '$_ = $_ . ($_ =~ s/$/.bak/r)'
# Compute a transformed version without touching $_
pperl -ne 'my $upper = $_ =~ tr/a-z/A-Z/r; print $_, $upper' file.txt
Καταφύγετε στο /r όταν μία γραμμή πρέπει να τυπωθεί σε δύο μορφές, ή όταν ο μετασχηματισμός είναι υπό συνθήκη και θέλετε την ανέπαφη γραμμή πίσω στον κλάδο αποτυχίας.
Ονομαστικές συλλήψεις#
Το (?<name>...) συλλαμβάνει στο $+{name} και στο %+.
# Parse combined-log-format access lines
pperl -nE '
if (/^(?<ip>\S+).*?"(?<method>\S+) (?<path>\S+)/) {
say "$+{ip} $+{method} $+{path}";
}
' access.log
Οι ονομαστικές συλλήψεις αξίζουν τον κόπο μόλις το μοτίβο έχει πάνω από δύο ομάδες, ή όταν η ίδια ομάδα χρειάζεται αναφορά τόσο στο μοτίβο όσο και στον κώδικα αντικατάστασης.
Πρόσθια και οπίσθια διεκδίκηση#
Διεκδικήσεις μηδενικού πλάτους: ταιριάζουν με μια θέση όπου το περιβάλλον κείμενο ικανοποιεί μια συνθήκη, χωρίς να καταναλώνουν την ίδια τη συνθήκη.
# Digits preceded by $ (e.g. "$42" → 42, ignore "42")
pperl -lne 'print for /(?<=\$)\d+/g' prices.txt
# Words NOT followed by "!"
pperl -lne 'print for /\b\w+\b(?!!)/g' shouts.txt
# Insert CRLF only where LF is not already preceded by CR
pperl -pe 's/(?<!\r)\n/\r\n/g' unix.txt
Συνηθισμένα ζεύγη:
Διεκδίκηση γύρω | Σημασία |
|---|---|
| Οι επόμενοι χαρακτήρες ταιριάζουν με |
| Οι επόμενοι χαρακτήρες δεν ταιριάζουν με |
| Οι προηγούμενοι χαρακτήρες ταιριάζουν με |
| Οι προηγούμενοι χαρακτήρες δεν ταιριάζουν με |
Η οπίσθια διεκδίκηση στο pperl υποστηρίζει μοτίβα μεταβλητού μήκους· δείτε κανονικές εκφράσεις: άγκυρες και διεκδικήσεις.
Πολυγραμμικές αντιστοιχίσεις σε ρουφηγμένη είσοδο#
Το -0777 ρουφάει ολόκληρο το αρχείο στο $_. Οι τροποποιητές regex που έχουν σημασία σε αυτή τη λειτουργία:
/s- το.ταιριάζει με\n./m- τα^και$ταιριάζουν σε κάθε αρχή/τέλος γραμμής, όχι μόνο στην αρχή/τέλος ολόκληρης της συμβολοσειράς.
# Every BEGIN...END block (non-greedy), each on one output line
pperl -0777 -lne '
while (/BEGIN(.*?)END/sg) {
(my $body = $1) =~ s/\n/ /g;
print $body;
}
' text.txt
# Paragraphs where every line starts with ">"
pperl -00 -ne 'print if /\A(?:>.*\n?)+\z/m' mail.txt
Τα \A και \z αγκυρώνουν την αρχή και το τέλος ολόκληρης της συμβολοσειράς, ανεπηρέαστα από το /m. Χρησιμοποιήστε τα όταν το /m είναι ενεργό αλλά εννοείτε πραγματικά ολόκληρη τη συμβολοσειρά.
Μεταγραμματισμός με tr#
Το tr (γραμμένο και ως y) είναι αντικατάσταση χαρακτήρα-προς-χαρακτήρα. Δεν είναι regex, αλλά κοντινός συγγενής - και το σωστό εργαλείο για δουλειές με σύνολα χαρακτήρων.
pperl -pe 'tr/A-Za-z/N-ZA-Mn-za-m/' # ROT13
pperl -pe 'tr/A-Za-z/a-zA-Z/' # swap case
pperl -pe 'tr/a-z//d' # delete lowercase letters
pperl -pe 'tr/ \t/ /s' # squeeze whitespace runs to one space
# Count commas on each line (tr returns the count)
pperl -ne 'print tr/,//, "\n"' data.csv
Η σημαία d διαγράφει χαρακτήρες του αριστερού συνόλου που δεν έχουν αντίστοιχο στα δεξιά. Η σημαία s συμπιέζει διαδοχές. Η σημαία c παίρνει το συμπλήρωμα του αριστερού συνόλου (ό,τι ΔΕΝ απαριθμείται).
Ανάλυση οριοθετημένων υποσυμβολοσειρών#
CSV με κόμματα σε εισαγωγικά#
Μια πρόχειρη διάσπαση που σέβεται τα πεδία σε διπλά εισαγωγικά:
pperl -ne '
my @F = /"([^"]*)"|([^,]+)/g;
@F = grep defined, @F;
print join("|", @F), "\n";
' quoted.csv
Σωστή και αρκετά γρήγορη για εφάπαξ χρήση. Για οτιδήποτε πρέπει να χειριστεί ενσωματωμένα εισαγωγικά ("he said ""hi"""), χρησιμοποιήστε Text::CSV· δείτε numeric.
Συμβολοσειρές ερωτημάτων URL#
# Each key=value pair on its own line
pperl -lne 'print for /([^?&=]+)=([^&]*)/g' urls.txt
Πρακτικά μονογραμμικά εξαγωγής#
Λέξεις κατά μήκος#
# Words of exactly 5 characters
pperl -lne 'print for /\b\w{5}\b/g' file.txt
Αριθμοί με μονάδες#
pperl -lne 'print for /\b\d+(?:\.\d+)?\s*(?:ms|s|kb|mb|gb)\b/gi' timings.txt
HTTP User-Agent από αρχείο καταγραφής αιτημάτων#
pperl -nE 'say $1 if /^User-Agent: (.+)$/' request.log
Ισοζυγισμένα άγκιστρα (όσα υποστηρίζει η μηχανή regex του pperl)#
Η μηχανή regex του pperl υποστηρίζει αναδρομή μέσω (?R) και (?1), άρα η ισοζύγιση αγκίστρων ενός επιπέδου είναι ένα μονόγραμμο:
pperl -0777 -nE 'say $& while /\{(?:[^{}]|(?R))*\}/g' code.txt
Για βαθιά εμφωλευμένες δομές, καταφύγετε σε αναλυτή. Το regex είναι το σωστό εργαλείο μέχρι το επίπεδο πολυπλοκότητας όπου πάψει να είναι.
Μάθετε περισσότερα#
οδηγός κανονικών εκφράσεων - η γλώσσα που υπόκειται κάθε συνταγής εδώ, συμπεριλαμβανομένων τροποποιητών, απόδοσης και Unicode.
m- ο τελεστής αντιστοίχισης.s- αντικατάσταση, το πλήρες σύνολο τροποποιητών.tr- αναφορά μεταγραμματισμού.progression - απλούστερες συνταγές
s///χωρίς συλλήψεις ή κώδικα.perlop- η μορφήqr//για προμεταγλώττιση μοτίβων που αναφέρονται με όνομα μέσα σε μονόγραμμο.