Elixir offre une syntaxe élégante pour manipuler des données binaires, comme on l'a vu avec la forme spéciale <<>> fournie pour travailler avec les bitstrings.
Le type binaire est une spécialisation du type bitstring. Là où un bitstring peut être de n'importe quelle longueur (n'importe quel nombre de bits), un binaire est une donnée dont le nombre de bits est divisible par 8. Autrement dit, quand on travaille avec des binaires, on raisonne souvent en octets (8 bits). Un octet peut représenter des nombres entiers de 0 à 255. Il est courant de manipuler des valeurs d'octets en hexadécimal, 0x00 - 0xFF.
Les littéraux binaires se définissent à l'aide de la forme spéciale <<>>. Pour définir un littéral binaire, on peut utiliser des littéraux entiers et des littéraux string. Les valeurs entières supérieures à 255 débordent et seuls les 8 derniers bits de l'entier sont utilisés. Par défaut, le modificateur ::binary est appliqué à la valeur. On peut concaténer des binaires avec l'opérateur <>/2.
<<255>> == <<0xFF>>
# Overflowing bits are truncated
<<256>> == <<0>>
<<2, 4, 6, 8, 10, 12, 14, 16>> == <<0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10>>
Un octet nul est un autre nom pour <<0>>.
Le filtrage par motif s'étend même aux binaires, et on peut appliquer un motif à une partie des données binaires, un peu comme on le ferait pour une liste.
# Ignore the first 8 bytes, match and bind the remaining to `body`
<<_::binary-size(8), body::binary>>
Comme pour les autres formes de filtrage par motif, on peut s'en servir dans la signature d'une fonction pour choisir parmi plusieurs clauses.
Tu travailles sur un projet qui permet aux utilisateurs de téléverser des fichiers sur le serveur pour les partager avec d'autres utilisateurs. On t'a chargé d'écrire une fonction pour vérifier qu'un fichier téléversé correspond à son type de média. Après quelques recherches, tu découvres que les premiers octets d'un fichier sont généralement propres à ce type de fichier, ce qui lui donne une sorte de signature.
Utilise le tableau suivant comme référence :
| Type de fichier | Extension courante | Type de média | « signature » binaire |
|---|---|---|---|
| ELF | "exe" |
"application/octet-stream" |
0x7F, 0x45, 0x4C, 0x46 |
| BMP | "bmp" |
"image/bmp" |
0x42, 0x4D |
| PNG | "png" |
"image/png" |
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A |
| JPG | "jpg" |
"image/jpg" |
0xFF, 0xD8, 0xFF |
| GIF | "gif" |
"image/gif" |
0x47, 0x49, 0x46 |
Implémente la fonction type_from_extension/1. Elle prend une extension de fichier (une string) et renvoie le type de média (string), ou nil si l'extension ne correspond pas à celles attendues.
FileSniffer.type_from_extension("exe")
# => "application/octet-stream"
FileSniffer.type_from_extension("txt")
# => nil
Implémente la fonction type_from_binary/1. Elle prend un fichier (binaire) et renvoie le type de média (string), ou nil si la signature du fichier ne correspond pas à celles attendues.
file = File.read!("application.exe")
FileSniffer.type_from_binary(file)
# => "application/octet-stream"
file = File.read!("example.txt")
FileSniffer.type_from_binary(file)
# => nil
Ne te soucie pas de lire le fichier en binaire. Considère que c'est déjà fait et que les tests te le passent en argument.
Implémente la fonction verify/2. Elle prend un fichier (binaire) et une extension (string), et renvoie un tuple :ok ou :error.
file = File.read!("application.exe")
FileSniffer.verify(file, "exe")
# => {:ok, "application/octet-stream"}
FileSniffer.verify(file, "png")
# => {:error, "Warning, file format and file extension do not match."}
Inscris-toi sur Exercism pour apprendre et maîtriser Elixir avec 58 concepts168 exercices, et un vrai mentorat humain, le tout gratuitement.