Οι κανονικές εκφράσεις είναι ένας εξαιρετικά ευέλικτος τρόπος για αντιστοίχιση μοτίβων σε συμβολοσειρές, χρησιμοποιώντας μια Γλώσσα Ειδικού Τομέα (DSL) σχεδιασμένη γι' αυτόν τον σκοπό.
Όπως αρκετές άλλες γλώσσες προγραμματισμού, η Julia δεν επιχειρεί να υλοποιήσει τη δική της βιβλιοθήκη κανονικών εκφράσεων. Αντ' αυτού, τυλίγει τη δημοφιλή βιβλιοθήκη PCRE2, παρέχοντας έτσι σύνταξη κανονικών εκφράσεων ίδια με (για παράδειγμα) τη Gleam, και πολύ παρόμοια με τη Javascript.
Αυτό το πρόγραμμα σπουδών της Julia υποθέτει ότι είσαι ήδη εξοικειωμένος με τη βασική σύνταξη κανονικών εκφράσεων. Θα επικεντρωθούμε αποκλειστικά σε χαρακτηριστικά ειδικά για τη Julia.
Παρακάτω παρατίθενται μερικές πηγές για να ανανεώσεις τις γνώσεις σου στις κανονικές εκφράσεις.
Η διεπαφή της Julia με τις κανονικές εκφράσεις περιγράφεται στο εγχειρίδιο.
Μια κανονική έκφραση στη Julia είναι απλώς μια συμβολοσειρά με το πρόθεμα r πριν από το αρχικό εισαγωγικό ".
Όλη η βασική λειτουργικότητα είναι μέρος της τυπικής βιβλιοθήκης.
Στην πραγματικότητα, πολλές από τις συναρτήσεις που έχουμε ήδη συζητήσει στην έννοια Strings είναι σχεδιασμένες εξ ορισμού για αναζητήσεις με κανονικές εκφράσεις, όπως η occursin().
julia> re = r"test$"
r"test$"
julia> typeof(re)
Regex
# Does a string end with "test"?
julia> occursin(re, "this is a test")
true
julia> occursin(re, "these are tests")
false
Χαρακτήρες τροποποίησης μπορούν να ακολουθούν το τελικό εισαγωγικό, όπως το i για αντιστοίχιση χωρίς διάκριση πεζών-κεφαλαίων.
julia> occursin(r"test", "Testing")
false
julia> occursin(r"test"i, "Testing")
true
Συχνά, θέλουμε να μάθουμε τι ταιριάζει. Αυτό επιτυγχάνεται με την προσθήκη ομάδων σύλληψης σε παρενθέσεις μέσα στην κανονική έκφραση και στη συνέχεια με τη χρήση της συνάρτησης match().
julia> m = match(r"(\d+g) .* (\d+ml)", "dissolve 25g sugar in 200ml water")
RegexMatch("25g sugar in 200ml", 1="25g", 2="200ml")
julia> m.captures
2-element Vector{Union{Nothing, SubString{String}}}:
"25g"
"200ml"
# how many matches?
julia> length(m.captures)
2
# what matched?
julia> m[1], m[2]
("25g", "200ml")
# Starting positions of the matches (character index)
julia> m.offsets
2-element Vector{Int64}:
10
23
Φυσικά, οι αντιστοιχίσεις μπορούν να αποτύχουν.
Το αποτέλεσμα τότε θα είναι η ειδική τιμή Nothing αντί για ένα RegexMatch, οπότε να είσαι έτοιμος να το ελέγξεις.
# failed match
m = match(r"(not here)", "dissolve 25g sugar in 200ml water")
julia> typeof(m)
Nothing
julia> isnothing(m)
true
Αν και η match ξεκινάει από προεπιλογή από την αρχή της συμβολοσειράς, μπορούμε επίσης να ορίσουμε μια μετατόπιση n ώστε να αγνοήσουμε τους πρώτους n χαρακτήρες.
# capture first match
julia> m = match(r"(\wat)", "cat, sat, mat")
RegexMatch("cat", 1="cat")
# ignore first 5 characters, then match
julia> m = match(r"(\wat)", "cat, sat, mat", 5)
RegexMatch("sat", 1="sat")
Στη Julia, η match() θα βρει μόνο την πρώτη αντιστοίχιση μέσα στη συμβολοσειρά-στόχο: δεν υπάρχει καθολικός τροποποιητής όπως σε ορισμένες άλλες γλώσσες.
Αντ' αυτού, έχουμε την eachmatch(), η οποία επιστρέφει έναν επαναλήπτη αντιστοιχίσεων.
Αυτός αξιολογείται οκνηρά, οπότε μπορεί να χρειαστεί να τον μετατρέψεις στη μορφή που θέλεις.
julia> matches = eachmatch(r"(\wat)", "cat, sat, mat")
Base.RegexMatchIterator{String}(r"(\wat)", "cat, sat, mat", false)
# convert to vector
julia> collect(matches)
3-element Vector{RegexMatch}:
RegexMatch("cat", 1="cat")
RegexMatch("sat", 1="sat")
RegexMatch("mat", 1="mat")
# convert with comprehension
julia> [m.match for m in matches]
3-element Vector{SubString{String}}:
"cat"
"sat"
"mat"
# broadcast an anonymous function
julia> (m -> m.match).(matches)
3-element Vector{SubString{String}}:
"cat"
"sat"
"mat"
Οι επικαλυπτόμενες αντιστοιχίσεις δεν επιτρέπονται από προεπιλογή.
Πρόσθεσε overlap = true ως όρισμα με λέξη-κλειδί για να το παρακάμψεις.
julia> eachmatch(r"aba", "abababa") |> collect # matches at positions 1, 5
2-element Vector{RegexMatch}:
RegexMatch("aba")
RegexMatch("aba")
julia> eachmatch(r"aba", "abababa"; overlap = true) |> collect # also matches at position 3
3-element Vector{RegexMatch}:
RegexMatch("aba")
RegexMatch("aba")
RegexMatch("aba")
Ένας συνηθισμένος λόγος για να χρησιμοποιήσεις μια κανονική έκφραση είναι να αντικαταστήσεις την αντιστοίχιση με μια διαφορετική συμβολοσειρά.
Η συνάρτηση replace() συζητήθηκε στην έννοια Strings, χρησιμοποιώντας κυριολεκτικές συμβολοσειρές για αναζήτηση.
Η ίδια συνάρτηση μπορεί να αξιοποιήσει όλη τη δύναμη της αντιστοίχισης με κανονικές εκφράσεις.
julia> replace("some string", r"[aeiou]" => "*")
"s*m* str*ng"
julia> replace("first second", r"(\w+) (?<agroup>\w+)" => s"\g<agroup> \1")
"second first"
Το δεύτερο παράδειγμα παραπάνω δείχνει πώς τόσο οι αριθμημένες όσο και οι ονομασμένες ομάδες σύλληψης μπορούν να χρησιμοποιηθούν στην αντικατάσταση, μέσα σε μια συμβολοσειρά s" ".
Δες το εγχειρίδιο για περισσότερες λεπτομέρειες: αυτό είναι ένα θέμα που αναγκάζει συνεχώς τους περισσότερους προγραμματιστές να επιστρέφουν στην τεκμηρίωση!