Διαδρομές
/
Elixir
Elixir
/
Ασκήσεις
/
Αναλυτής ημερομηνιών
Αναλυτής ημερομηνιών

Αναλυτής ημερομηνιών

Άσκηση εκμάθησης

Εισαγωγή

Κανονικές εκφράσεις

Οι κανονικές εκφράσεις (regex) είναι ένα ισχυρό εργαλείο για την εργασία με συμβολοσειρές στην Elixir. Οι κανονικές εκφράσεις στην Elixir ακολουθούν την προδιαγραφή PCRE (Perl Compatible Regular Expressions). Τα μοτίβα συμβολοσειρών που αντιπροσωπεύουν τη σημασία της κανονικής έκφρασης πρώτα μεταγλωττίζονται και στη συνέχεια χρησιμοποιούνται για το ταίριασμα όλης ή μέρους μιας συμβολοσειράς.

Στην Elixir, ο πιο συνηθισμένος τρόπος για να δημιουργήσεις κανονικές εκφράσεις είναι με το sigil ~r. Τα sigils παρέχουν συντακτική ζάχαρη, δηλαδή συντομεύσεις για συνηθισμένες εργασίες στην Elixir. Για να ταιριάξεις μια κυριολεκτική συμβολοσειρά, μπορείς να χρησιμοποιήσεις την ίδια τη συμβολοσειρά ως μοτίβο μετά το sigil.

~r/test/

Ο τελεστής =~/2 είναι χρήσιμος για να εκτελέσεις ένα ταίριασμα κανονικής έκφρασης σε μια συμβολοσειρά, το οποίο επιστρέφει ένα αποτέλεσμα boolean.

"this is a test" =~ ~r/test/
# => true

Δύο σημειώσεις για τη χρήση των sigils:

  • μπορούν να χρησιμοποιηθούν πολλοί διαφορετικοί οριοθέτες ανάλογα με τις ανάγκες σου, αντί για το /
  • τα μοτίβα συμβολοσειρών είναι ήδη escaped· όταν γράφεις το μοτίβο ως συμβολοσειρά χωρίς κανονική έκφραση, θα πρέπει να κάνεις escape στις ανάστροφες καθέτους (\)

Κλάσεις χαρακτήρων

Το ταίριασμα ενός εύρους χαρακτήρων με αγκύλες [] ορίζει μια κλάση χαρακτήρων. Αυτή ταιριάζει έναν οποιονδήποτε χαρακτήρα με τους χαρακτήρες της κλάσης. Μπορείς επίσης να ορίσεις ένα εύρος χαρακτήρων όπως a-z, αρκεί η αρχή και το τέλος να αντιπροσωπεύουν ένα συνεχές εύρος σημείων κώδικα.

regex = ~r/[a-z][ADKZ][0-9][!?]/
"jZ5!" =~ regex
# => true
"jB5?" =~ regex
# => false

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

  • \d συντομογραφία του [0-9] (οποιοδήποτε ψηφίο)
  • \w συντομογραφία του [A-Za-z0-9_] (οποιοσδήποτε χαρακτήρας "λέξης")
  • \s συντομογραφία του [ \t\r\n\f] (οποιοσδήποτε χαρακτήρας κενού)

Όταν μια συντομευμένη κλάση χαρακτήρων χρησιμοποιείται έξω από ένα sigil, πρέπει να γίνει escape: "\\d"

Εναλλαγές

Οι εναλλαγές χρησιμοποιούν το | ως ειδικό χαρακτήρα για να δηλώσουν ότι ταιριάζει το ένα ή το άλλο

regex = ~r/cat|bat/
"bat" =~ regex
# => true
"cat" =~ regex
# => true

Ποσοδείκτες

Οι ποσοδείκτες επιτρέπουν ένα επαναλαμβανόμενο μοτίβο στην κανονική έκφραση. Επηρεάζουν την ομάδα που προηγείται του ποσοδείκτη.

  • {N, M} όπου N είναι ο ελάχιστος αριθμός επαναλήψεων και M ο μέγιστος
  • {N,} ταιριάζει N ή περισσότερες επαναλήψεις
    • {0,} μπορεί επίσης να γραφτεί ως *: ταιριάζει μηδέν ή περισσότερες επαναλήψεις
    • {1,} μπορεί επίσης να γραφτεί ως +: ταιριάζει μία ή περισσότερες επαναλήψεις
  • {,N} ταιριάζει μέχρι N επαναλήψεις

Ομάδες

Οι παρενθέσεις () χρησιμοποιούνται για να δηλώσουν ομάδες και συλλήψεις. Η ομάδα μπορεί επίσης να συλληφθεί σε ορισμένες περιπτώσεις ώστε να επιστραφεί για χρήση. Στην Elixir, αυτές μπορεί να είναι ονομασμένες ή ανώνυμες. Οι συλλήψεις ονομάζονται προσθέτοντας ?<name> μετά την παρένθεση ανοίγματος. Οι ομάδες λειτουργούν ως μία μονάδα, όπως όταν ακολουθούνται από ποσοδείκτες.

regex = ~r/(h)at/
Regex.replace(regex, "hat", "\\1op")
# => "hop"

regex = ~r/(?<letter_b>b)/
Regex.scan(regex, "blueberry", capture: :all_names)
# => [["b"], ["b"]]

Άγκυρες

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

  • ^ αγκυρώνει στην αρχή της συμβολοσειράς
  • $ αγκυρώνει στο τέλος της συμβολοσειράς

Παρεμβολή

Επειδή το ~r είναι μια συντόμευση για το "pattern" |> Regex.escape() |> Regex.compile!(), μπορείς επίσης να χρησιμοποιήσεις παρεμβολή συμβολοσειρών για να χτίσεις δυναμικά ένα μοτίβο κανονικής έκφρασης:

anchor = "$"
regex = ~r/end of the line#{anchor}/
"end of the line?" =~ regex
# => false
"end of the line" =~ regex
# => true

Οδηγίες

Σου έχει ανατεθεί να γράψεις μια υπηρεσία που δέχεται συμβάντα. Κάθε συμβάν έχει συνδεδεμένη μια ημερομηνία, αλλά παρατηρείς ότι τρεις διαφορετικές μορφές υποβάλλονται στο endpoint της υπηρεσίας σου:

  • "01/01/1970"
  • "January 1, 1970"
  • "Thursday, January 1, 1970"

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

1. Αντιστοίχισε την ημέρα, τον μήνα και το έτος από μια ημερομηνία

Υλοποίησε τις day/0, month/0 και year/0 ώστε να επιστρέφουν ένα μοτίβο συμβολοσειράς το οποίο, όταν μεταγλωττιστεί, θα αντιστοιχίζει τα αριθμητικά μέρη του "01/01/1970" (dd/mm/yyyy). Η ημέρα και ο μήνας μπορεί να εμφανίζονται ως 1 ή 01 (με μηδενικά στα αριστερά).

"31" =~ DateParser.day() |> Regex.compile!()
# => true
"12" =~ DateParser.month() |> Regex.compile!()
# => true
"1970" =~ DateParser.year() |> Regex.compile!()
# => true

2. Αντιστοίχισε την ημέρα της εβδομάδας και τον μήνα του έτους

Υλοποίησε τις day_names/0 και month_names/0 ώστε να επιστρέφουν ένα μοτίβο συμβολοσειράς το οποίο, όταν μεταγλωττιστεί, θα αντιστοιχίζει αντίστοιχα το όνομα της ημέρας της εβδομάδας και το όνομα του μήνα του έτους.

"Tuesday" =~ DateParser.day_names() |> Regex.compile!()
# => true
"June" =~ DateParser.month_names() |> Regex.compile!()
# => true

3. Συνέλαβε την ημέρα, τον μήνα και το έτος

Υλοποίησε τις capture_day/0, capture_month/0, capture_year/0, capture_day_name/0, capture_month_name/0 ώστε να επιστρέφουν ένα μοτίβο συμβολοσειράς που συλλαμβάνει τα αντίστοιχα μέρη με τις ονομασίες: "day", "month", "year", "day_name", "month_name"

DateParser.capture_month_name()
|> Regex.compile!()
|> Regex.named_captures("December")

# => %{"month_name" => "December"}

4. Συνδύασε τις συλλήψεις για να συλλάβεις ολόκληρη την ημερομηνία

Υλοποίησε τις capture_numeric_date/0, capture_month_name_date(), και capture_day_month_name_date/0 ώστε να επιστρέφουν ένα μοτίβο συμβολοσειράς που συλλαμβάνει τα μέρη από το μέρος 3 χρησιμοποιώντας την αντίστοιχη μορφή ημερομηνίας:

  • αριθμητική ημερομηνία - "01/01/1970"
  • ημερομηνία με όνομα μήνα - "January 1, 1970"
  • ημερομηνία με όνομα ημέρας και μήνα - "Thursday, January 1, 1970"
DateParser.capture_numeric_date()
|> Regex.compile!()
|> Regex.named_captures("01/01/1970")

# => %{"day" => "01", "month" => "01", "year" => "1970"}

5. Περιόρισε τη σύλληψη ώστε να αντιστοιχίζει μόνο την ημερομηνία

Υλοποίησε τις match_numeric_date/0, match_month_name_date/0 και match_day_month_name_date/0 ώστε να επιστρέφουν μια μεταγλωττισμένη κανονική έκφραση που αντιστοιχίζει μόνο την ημερομηνία και μπορεί επίσης να συλλάβει τα μέρη.

"Thursday, January 1, 1970 was the Unix epoch." =~ DateParser.match_day_month_name_date()
# => false
"Thursday, January 1, 1970" =~ DateParser.match_day_month_name_date()
# => true

DateParser.match_day_month_name_date()
|> Regex.named_captures("Thursday, January 1, 1970")

# => %{
#     "day" => "1",
#     "day_name" => "Thursday",
#     "month_name" => "January",
#     "year" => "1970"
#   }
Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα
Elixir Exercism

Έτοιμος να ξεκινήσεις την άσκηση Αναλυτής ημερομηνιών;

Γράψου στο Exercism για να μάθεις και να κατακτήσεις Elixir με 58 έννοιες168 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.