Οι κανονικές εκφράσεις (regexes) είναι ακολουθίες χαρακτήρων που καθορίζουν ένα μοτίβο αναζήτησης σε κείμενο.
Η εκμάθηση της σύνταξης των κανονικών εκφράσεων ξεφεύγει από το πεδίο αυτού του θέματος.
Θα επικεντρωθούμε στις εκφράσεις που παρέχει το jq για να χρησιμοποιήσεις τις κανονικές εκφράσεις.
Διαφορετικά εργαλεία υλοποιούν διαφορετικές εκδόσεις των κανονικών εκφράσεων.
Το jq ενσωματώνει τη βιβλιοθήκη κανονικών εκφράσεων Oniguruma, η οποία είναι σε μεγάλο βαθμό συμβατή με τις κανονικές εκφράσεις της Perl v5.8.
Η συγκεκριμένη σύνταξη που χρησιμοποιεί το jq βρίσκεται στο αποθετήριο GitHub του Oniguruma.
Το jq δεν έχει κάποια ειδική σύνταξη για τις κανονικές εκφράσεις.
Εκφράζονται απλώς ως συμβολοσειρές.
Αυτό σημαίνει ότι τυχόν ανάστροφες κάθετοι μέσα στην κανονική έκφραση πρέπει να διαφύγουν μέσα στη συμβολοσειρά.
Για παράδειγμα, η κλάση χαρακτήρων ψηφίου (\d) πρέπει να γραφτεί ως "\\d".
Οι κανονικές εκφράσεις στο jq περιορίζονται σε ένα σύνολο φίλτρων.
Όταν χρειάζεται να ξέρεις αν μια συμβολοσειρά ταιριάζει με ένα μοτίβο, χρησιμοποίησε το φίλτρο test.
STRING | test(REGEX)
STRING | test(REGEX; FLAGS)
STRING | test([REGEX, FLAGS])
Αυτό το φίλτρο βγάζει ένα boolean (λογική τιμή) αποτέλεσμα.
"Hello World!" | test("W") # => true
"Goodbye Mars" | test("W") # => false
Όταν χρειάζεται να εξαγάγεις την υποσυμβολοσειρά που όντως ταίριαξε με το μοτίβο, χρησιμοποίησε το φίλτρο match.
STRING | match(REGEX)
STRING | match(REGEX; FLAGS)
STRING | match([REGEX, FLAGS])
Αυτό το φίλτρο βγάζει:
Αυτό το παράδειγμα ψάχνει για δύο ίδια συνεχόμενα φωνήεντα χρησιμοποιώντας τη σύνταξη οπισθοαναφοράς, \1.
"Hello World!" | match("([aeiou])\\1")
# => empty
"Goodbye Mars" | match("([aeiou])\\1")
# => {
# "offset": 1,
# "length": 2,
# "string": "oo",
# "captures": [
# {
# "offset": 1,
# "length": 1,
# "string": "o",
# "name": null
# }
# ]
# }
Το φίλτρο match επιστρέφει ένα αντικείμενο για κάθε αντιστοίχιση.
Αυτό το παράδειγμα δείχνει τη σημαία "g" εν δράσει, για να βρει όλα τα φωνήεντα.
"Goodbye Mars" | match("[aeiou]"; "g")
# => { "offset": 1, "length": 1, "string": "o", "captures": [] }
# { "offset": 2, "length": 1, "string": "o", "captures": [] }
# { "offset": 6, "length": 1, "string": "e", "captures": [] }
# { "offset": 9, "length": 1, "string": "a", "captures": [] }
Παρόμοια με το φίλτρο match, το φίλτρο capture επιστρέφει ένα αντικείμενο αν υπήρξε αντιστοίχιση.
STRING | capture(REGEX)
STRING | capture(REGEX; FLAGS)
STRING | capture([REGEX, FLAGS])
Το αντικείμενο που επιστρέφεται είναι μια αντιστοίχιση των ονομασμένων συλλήψεων.
"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")
# => {
# "project": "JIRAISSUE",
# "issue_num": "1234"
# }
Το φίλτρο scan είναι παρόμοιο με το match με τη σημαία "g".
STRING | scan(REGEX)
STRING | scan(REGEX; FLAGS)
Το scan θα βγάλει μια ροή υποσυμβολοσειρών.
"Goodbye Mars" | scan("[aeiou]")
# => "o"
# "o"
# "e"
# "a"
Χρησιμοποίησε τον κατασκευαστή πίνακα [...] για να συλλάβεις τις υποσυμβολοσειρές.
"Goodbye Mars" | [ scan("[aeiou]") ]
# => ["o", "o", "e", "a"]
Αν ξέρεις τα μέρη της συμβολοσειράς που θέλεις να κρατήσεις, χρησιμοποίησε το match ή το scan.
Αν ξέρεις τα μέρη που θέλεις να απορρίψεις, χρησιμοποίησε το split.
STRING | split(REGEX; FLAGS)
Το φίλτρο split με ένα όρισμα αντιμετωπίζει το όρισμά του ως σταθερή συμβολοσειρά.
Για να χρησιμοποιήσεις κανονική έκφραση με το split, πρέπει να δώσεις το 2ο όρισμα. Δεν πειράζει να χρησιμοποιήσεις μια κενή συμβολοσειρά.
Ένα παράδειγμα που χωρίζει μια συμβολοσειρά σε αυθαίρετα κενά διαστήματα.
"first second third fourth" | split("\\s+"; "")
# => ["first", "second", "third", "fourth"]
Τα φίλτρα sub και gsub μπορούν να μετασχηματίσουν τη συμβολοσειρά εισόδου, αντικαθιστώντας τα τμήματα της εισόδου που αντιστοιχίστηκαν με μια συμβολοσειρά αντικατάστασης.
Για να αντικαταστήσεις μόνο την πρώτη αντιστοίχιση, χρησιμοποίησε το sub.
Για να αντικαταστήσεις όλες τις αντιστοιχίσεις, χρησιμοποίησε το gsub.
STRING | sub(REGEX; REPLACEMENT)
STRING | sub(REGEX; REPLACEMENT; FLAGS)
STRING | gsub(REGEX; REPLACEMENT)
STRING | gsub(REGEX; REPLACEMENT; FLAGS)
"Goodnight kittens. Goodnight mittens." | sub("night"; " morning")
# => "Good morning kittens. Goodnight mittens."
"Goodnight kittens. Goodnight mittens." | gsub("night"; " morning")
# => "Good morning kittens. Good morning mittens."
Το κείμενο αντικατάστασης μπορεί να αναφέρεται στις υποσυμβολοσειρές που αντιστοιχίστηκαν. Χρησιμοποίησε ονομασμένες συλλήψεις και παρεμβολή συμβολοσειράς.
"Some 3-letter acronyms: gnu, csv, png"
| gsub( "\\b(?<tla>[[:alpha:]]{3})\\b"; # find words 3 letters long
"\(.tla | ascii_upcase)" ) # upper-case the match
# => "Some 3-letter acronyms: GNU, CSV, PNG"
Σε όλα τα παραπάνω φίλτρα, το FLAGS είναι μια συμβολοσειρά που αποτελείται από μηδέν ή περισσότερες από τις υποστηριζόμενες σημαίες.
g - Καθολική αναζήτηση (βρες όλες τις αντιστοιχίσεις, όχι μόνο την πρώτη)i - Αναζήτηση χωρίς διάκριση πεζών/κεφαλαίωνm - Λειτουργία πολλαπλών γραμμών (το '.' θα ταιριάζει με αλλαγές γραμμής)n - Αγνόησε τις κενές αντιστοιχίσειςp - Ενεργοποιούνται και οι δύο λειτουργίες s και ms - Λειτουργία μονής γραμμής ('^' -> '\A', '$' -> '\Z')l - Βρες τις μεγαλύτερες δυνατές αντιστοιχίσειςx - Εκτεταμένη μορφή κανονικών εκφράσεων (αγνόησε τα κενά και τα σχόλια)Για παράδειγμα
"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")
# or with Extended formatting
"JIRAISSUE-1234" | capture("
(?<project> \\w+ ) # the Jira project
- # followed by a hyphen
(?<issue_num> \\d+ ) # followed by digits
"; "x")
Έχεις προσληφθεί ως Ειδικός Κανονικών Εκφράσεων σε μια εταιρεία που αναπτύσσει ένα Chatbot.
Βρίσκεται σε ένα πολύ αρχικό στάδιο ανάπτυξης. Η αποστολή σου είναι να χρησιμοποιήσεις Κανονικές Εκφράσεις για να βελτιώσεις την ικανότητα του Chatbot να κατανοεί και να παράγει φυσική γλώσσα.
Πέρα από έξυπνο, το Chatbot είναι και πιστός βοηθός. Για να ζητήσει κάποιος κάτι από το Chatbot, πρέπει να πει τη λέξη "Chatbot" στην πρώτη θέση της εντολής. Δεν έχει σημασία αν η λέξη-κλειδί είναι γραμμένη με ΚΕΦΑΛΑΙΑ ή με πεζά. Το σημαντικό είναι η θέση της λέξης.
Υλοποίησε τη συνάρτηση is_valid_command που βοηθά το Chatbot να αναγνωρίζει πότε ο χρήστης δίνει μια εντολή.
"Chatbot, play a song from the 80's." | is_valid_command
# => true
"Hey Chatbot, where is the closest pharmacy?" | is_valid_command
# => false
"CHATBOT, do you have a solution for this challenge?" | is_valid_command
# => true
Το Chatbot δυσκολεύεται να καταλάβει πώς οι άνθρωποι χρησιμοποιούν τα emoji για να εκφράσουν τα συναισθήματά τους. Όταν το Chatbot λαμβάνει μηνύματα από χρήστες, κάθε emoji αναπαρίσταται ως η συμβολοσειρά "emoji" ακολουθούμενη από έναν αριθμό id.
Υλοποίησε τη μέθοδο remove_emoji που παίρνει μια συμβολοσειρά και αφαιρεί όλα τα emoji από το μήνυμα.
Οι γραμμές που δεν περιέχουν emoji θα πρέπει να επιστρέφονται αμετάβλητες. Απλώς αφαίρεσε τη συμβολοσειρά "emoji". Μην πειράξεις τα κενά.
"I love playing videogames emoji3465 it's one of my hobbies" | remove_emoji
# => "I love playing videogames it's one of my hobbies"
Κάποια στιγμή στην αλληλεπίδραση με το Chatbot, ο χρήστης θα δώσει έναν αριθμό τηλεφώνου. Το Chatbot μπορεί να καλέσει μόνο αριθμούς με συγκεκριμένη μορφή.
Υλοποίησε τη συνάρτηση check_phone_number.
Αν ο αριθμός είναι έγκυρος, το Chatbot απαντά με ένα μήνυμα που ευχαριστεί τον χρήστη και επιβεβαιώνει τον αριθμό. Αν ο αριθμός δεν είναι έγκυρος, το Chatbot ενημερώνει τον χρήστη ότι ο αριθμός τηλεφώνου δεν είναι έγκυρος.
Η αναμενόμενη μορφή είναι (+NN) NNN-NNN-NNN, όπου το N είναι ένα ψηφίο.
"chatbot my phone number is (+34) 659-771-594" | check_phone_number
# => "Thanks! Your phone number is OK."
"chatbot, call me at 659-771-594" | check_phone_number
# => "Oops, it seems like I can't reach out to 659-771-594."
Το Chatbot είναι ένα λογισμικό με μεγάλη περιέργεια. Παρόλο που μπορεί να ψάξει στο διαδίκτυο για ένα συγκεκριμένο θέμα, του αρέσει να ρωτά τους χρήστες για ωραίες ιστοσελίδες που μπορεί να επισκεφτεί, ώστε να βρει σχετικές πληροφορίες.
Παράδειγμα συνομιλίας:
Chatbot: Γεια σου χρήστη, θα ήθελα να μάθω πώς να προγραμματίζω σε JavaScript. Ξέρεις καμία ωραία ιστοσελίδα όπου θα μπορούσα να μάθω;
Χρήστης: Έμαθα πολλά από το exercism.org, έχει πολύ ωραίο υλικό εκεί.
Υλοποίησε τη συνάρτηση get_domains που επιστρέφει έναν πίνακα με domains ιστοσελίδων.
"I learned a lot from exercism.org and google.com" | get_domains
# => ["exercism.org", "google.com"]
Ένα ευγενικό Chatbot μιλά στους χρήστες με το όνομά τους. Όταν ένας χρήστης συστήνεται, το Chatbot μας εντοπίζει το όνομά του και απαντά με έναν φιλικό χαιρετισμό.
Γράψε τη συνάρτηση nice_to_meet_you.
Αν η συμβολοσειρά εισόδου περιέχει "My name is Someone.", πιάσε το όνομα και επέστρεψε τη συμβολοσειρά "Nice to meet you, Someone.".
"My name is Jean-Luc" | nice_to_meet_you
# => "Nice to meet you, Jean-Luc"
Υποκύπτοντας στον πειρασμό να προσθέσουμε κι άλλες λειτουργίες, θα προσθέσουμε στο Chatbot μια συνάρτηση ανάλυσης CSV.
Υλοποίησε τη συνάρτηση parse_csv που παίρνει μια συμβολοσειρά και επιστρέφει έναν πίνακα με τα πεδία που προκύπτουν.
Ο διαχωριστής πεδίων θα πρέπει να είναι "κόμμα μαζί με προαιρετικά κενά".
Δεν θα ασχοληθούμε με τις οριακές περιπτώσεις της μορφής CSV (όπως πεδία που περιέχουν κόμματα).
"first, second,third, fourth" | parse_csv
# => ["first", "second", "third", "fourth"]
Γράψου στο Exercism για να μάθεις και να κατακτήσεις jq με 12 έννοιες75 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.