Parcours
/
Elixir
Elixir
/
Exercices
/
Analyseur de dates
Analyseur de dates

Analyseur de dates

Exercice d'apprentissage

Introduction

Les expressions régulières

Les expressions régulières (regex) sont un outil puissant pour travailler avec des strings en Elixir. Les expressions régulières en Elixir suivent la spécification PCRE (Perl Compatible Regular Expressions). Les motifs de string qui représentent la signification de l'expression régulière sont d'abord compilés, puis utilisés pour rechercher une correspondance sur tout ou partie d'une string.

En Elixir, la méthode la plus courante pour créer des expressions régulières consiste à utiliser le sigil ~r. Les sigils offrent des raccourcis de sucre syntaxique pour les tâches courantes en Elixir. Pour faire correspondre un littéral de chaîne de caractères, on peut utiliser la string elle-même comme motif à la suite du sigil.

~r/test/

L'opérateur =~/2 est utile pour effectuer une correspondance d'expression régulière sur une string et renvoyer un résultat boolean.

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

Deux remarques sur l'utilisation des sigils :

  • de nombreux délimiteurs différents peuvent être utilisés selon tes besoins, plutôt que /
  • les motifs de string sont déjà échappés ; lorsque tu écris le motif sous forme de string sans passer par une expression régulière, tu devras échapper les barres obliques inverses (\)

Les classes de caractères

Faire correspondre une plage de caractères à l'aide de crochets [] définit une classe de caractères. Elle permet de faire correspondre n'importe quel caractère parmi ceux de la classe. Tu peux aussi indiquer une plage de caractères comme a-z, tant que le début et la fin représentent une plage contiguë de points de code.

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

Les classes de caractères abrégées rendent le motif plus concis. Par exemple :

  • \d est l'abréviation de [0-9] (n'importe quel chiffre)
  • \w est l'abréviation de [A-Za-z0-9_] (n'importe quel caractère de « mot »)
  • \s est l'abréviation de [ \t\r\n\f] (n'importe quel caractère d'espacement)

Lorsqu'une classe de caractères abrégée est utilisée en dehors d'un sigil, elle doit être échappée : "\\d"

L'alternance

L'alternance utilise | comme caractère spécial pour indiquer la correspondance avec l'un ou l'autre.

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

Les quantificateurs

Les quantificateurs permettent de répéter un motif dans l'expression régulière. Ils s'appliquent au groupe qui précède le quantificateur.

  • {N, M} où N est le nombre minimum de répétitions et M le maximum
  • {N,} correspond à N répétitions ou plus
    • {0,} peut aussi s'écrire * : correspond à zéro répétition ou plus
    • {1,} peut aussi s'écrire + : correspond à une répétition ou plus
  • {,N} correspond à N répétitions au maximum

Les groupes

Les parenthèses () servent à désigner les groupes et les captures. Dans certains cas, le groupe peut aussi être capturé pour être renvoyé et utilisé. En Elixir, ils peuvent être nommés ou anonymes. Une capture est nommée en ajoutant ?<name> après la parenthèse ouvrante. Les groupes fonctionnent comme une seule unité, par exemple lorsqu'ils sont suivis de quantificateurs.

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

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

Les ancres

Les ancres servent à fixer l'expression régulière au début ou à la fin de la string à faire correspondre :

  • ^ ancre au début de la string
  • $ ancre à la fin de la string

L'interpolation

Comme ~r est un raccourci pour "pattern" |> Regex.escape() |> Regex.compile!(), tu peux aussi utiliser l'interpolation de string pour construire dynamiquement un motif d'expression régulière :

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

Instructions

Tu as pour mission d'écrire un service qui ingère des événements. Chaque événement est associé à une date, mais tu remarques que 3 formats différents sont envoyés au point d'accès de ton service :

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

Tu vois qu'il existe des similitudes entre eux et tu décides d'écrire des motifs d'expressions régulières composables.

1. Fais correspondre le jour, le mois et l'année d'une date

Implémente day/0, month/0 et year/0 pour renvoyer un motif de string qui, une fois compilé, correspondrait aux composants numériques de "01/01/1970" (dd/mm/yyyy). Le jour et le mois peuvent apparaître sous la forme 1 ou 01 (complétés à gauche par des zéros).

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

2. Fais correspondre le jour de la semaine et le mois de l'année

Implémente day_names/0 et month_names/0 pour renvoyer un motif de string qui, une fois compilé, correspondrait respectivement au nom du jour de la semaine et au nom du mois de l'année.

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

3. Capture le jour, le mois et l'année

Implémente capture_day/0, capture_month/0, capture_year/0, capture_day_name/0, capture_month_name/0 pour renvoyer un motif de string qui capture les composants respectifs dans les noms : "day", "month", "year", "day_name", "month_name"

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

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

4. Combine les captures pour capturer la date entière

Implémente capture_numeric_date/0, capture_month_name_date(), et capture_day_month_name_date/0 pour renvoyer un motif de string qui capture les composants de la partie 3 en utilisant le format de date correspondant :

  • date numérique - "01/01/1970"
  • date avec le nom du mois - "January 1, 1970"
  • date avec le jour et le nom du mois - "Thursday, January 1, 1970"
DateParser.capture_numeric_date()
|> Regex.compile!()
|> Regex.named_captures("01/01/1970")

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

5. Restreins la capture pour qu'elle ne corresponde qu'à la date

Implémente match_numeric_date/0, match_month_name_date/0, et match_day_month_name_date/0 pour renvoyer une expression régulière compilée qui ne correspond qu'à la date et qui peut aussi capturer les composants.

"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"
#   }
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet
Elixir Exercism

Prêt à commencer Analyseur de dates ?

Inscris-toi sur Exercism pour apprendre et maîtriser Elixir avec 58 concepts168 exercices, et un vrai mentorat humain, le tout gratuitement.