Track
/
Elixir
Elixir
/
Esercizi
/
Parser di date
Parser di date

Parser di date

Esercizio di apprendimento

Introduzione

Espressioni regolari

Le espressioni regolari (regex) sono uno strumento potente per lavorare con le stringhe in Elixir. Le espressioni regolari in Elixir seguono la specifica PCRE (Perl Compatible Regular Expressions). I pattern di stringa che rappresentano il significato dell'espressione regolare vengono prima compilati e poi usati per trovare corrispondenze in tutta o parte di una stringa.

In Elixir, il modo più comune per creare espressioni regolari è usare il sigillo ~r. I sigilli forniscono scorciatoie di zucchero sintattico per attività comuni in Elixir. Per trovare una stringa letterale, possiamo usare la stringa stessa come pattern dopo il sigillo.

~r/test/

L'operatore =~/2 è utile per eseguire una corrispondenza regex su una stringa e restituire un risultato boolean.

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

Due note sull'uso dei sigilli:

  • si possono usare molti delimitatori diversi a seconda delle tue esigenze, non solo /
  • i pattern di stringa sono già sottoposti a escape; quando scrivi il pattern come stringa senza usare una regex, dovrai eseguire l'escape dei backslash (\)

Classi di caratteri

La corrispondenza di un intervallo di caratteri tramite parentesi quadre ([]) definisce una classe di caratteri. Questa corrisponde a un singolo carattere qualsiasi tra i caratteri della classe. Puoi anche specificare un intervallo di caratteri come a-z, purché l'inizio e la fine rappresentino un intervallo contiguo di punti di codice.

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

Le classi di caratteri abbreviate rendono il pattern più conciso. Per esempio:

  • \d è l'abbreviazione di [0-9] (qualsiasi cifra)
  • \w è l'abbreviazione di [A-Za-z0-9_] (qualsiasi carattere «di parola»)
  • \s è l'abbreviazione di [ \t\r\n\f] (qualsiasi carattere di spaziatura)

Quando una classe di caratteri abbreviata viene usata fuori da un sigillo, deve essere sottoposta a escape: "\\d"

Alternanze

Le alternanze usano | come carattere speciale per indicare la corrispondenza con uno o l'altro

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

Quantificatori

I quantificatori consentono un pattern ripetuto nella regex. Influenzano il gruppo che precede il quantificatore.

  • {N, M} dove N è il numero minimo di ripetizioni e M è il massimo
  • {N,} corrisponde a N o più ripetizioni
    • {0,} può anche essere scritto come *: corrisponde a zero o più ripetizioni
    • {1,} può anche essere scritto come +: corrisponde a una o più ripetizioni
  • {,N} corrisponde fino a N ripetizioni

Gruppi

Le parentesi tonde (()) sono usate per indicare gruppi e catture. Il gruppo può anche essere catturato in alcuni casi per essere restituito e usato. In Elixir, possono essere con nome o senza nome. Le catture vengono nominate aggiungendo ?<name> dopo la parentesi aperta. I gruppi funzionano come un'unica unità, come quando sono seguiti da quantificatori.

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

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

Ancore

Le ancore sono usate per ancorare l'espressione regolare all'inizio o alla fine della stringa da confrontare:

  • ^ ancora all'inizio della stringa
  • $ ancora alla fine della stringa

Interpolazione

Poiché ~r è una scorciatoia per "pattern" |> Regex.escape() |> Regex.compile!(), puoi anche usare l'interpolazione di stringhe per costruire dinamicamente un pattern di espressione regolare:

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

Istruzioni

Il tuo compito è scrivere un servizio che riceve eventi. Ogni evento ha una data associata, ma ti accorgi che al tuo endpoint vengono inviati 3 formati diversi:

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

Noti che ci sono alcune somiglianze tra loro e decidi di scrivere delle espressioni regolari componibili.

1. Trova il giorno, il mese e l'anno da una data

Implementa day/0, month/0 e year/0 in modo che restituiscano un pattern di stringa che, una volta compilato, corrisponda alle componenti numeriche in "01/01/1970" (dd/mm/yyyy). Il giorno e il mese possono comparire come 1 o 01 (riempiti a sinistra con degli zeri).

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

2. Trova il giorno della settimana e il mese dell'anno

Implementa day_names/0 e month_names/0 in modo che restituiscano un pattern di stringa che, una volta compilato, corrisponda rispettivamente al giorno della settimana e al mese dell'anno indicati per nome.

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

3. Cattura il giorno, il mese e l'anno

Implementa capture_day/0, capture_month/0, capture_year/0, capture_day_name/0 e capture_month_name/0 in modo che restituiscano un pattern di stringa che cattura le rispettive componenti assegnando loro i nomi: "day", "month", "year", "day_name", "month_name"

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

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

4. Combina le catture per catturare l'intera data

Implementa capture_numeric_date/0, capture_month_name_date() e capture_day_month_name_date/0 in modo che restituiscano un pattern di stringa che cattura le componenti della parte 3 usando il rispettivo formato di data:

  • data numerica: "01/01/1970"
  • data con il nome del mese: "January 1, 1970"
  • data con giorno e nome del mese: "Thursday, January 1, 1970"
DateParser.capture_numeric_date()
|> Regex.compile!()
|> Regex.named_captures("01/01/1970")

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

5. Restringi la cattura per corrispondere solo alla data

Implementa match_numeric_date/0, match_month_name_date/0 e match_day_month_name_date/0 in modo che restituiscano un'espressione regolare compilata che corrisponde solo alla data e che riesce anche a catturare le componenti.

"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"
#   }
Modifica tramite GitHub Il link si apre in una nuova finestra o scheda
Elixir Exercism

Vuoi iniziare Parser di date?

Iscriviti a Exercism per imparare e padroneggiare Elixir con 58 concetti168 esercizi e il mentoring di persone reali, tutto gratis.