Elixir offre una sintassi elegante per lavorare con i dati binari, come abbiamo visto con la forma speciale <<>> pensata per lavorare con i bitstring.
Il tipo binary è una specializzazione del tipo bitstring. I bitstring possono avere qualsiasi lunghezza (qualsiasi numero di bit); i binari, invece, sono quelli in cui il numero di bit è divisibile per 8. Quando lavoriamo con i binari, infatti, pensiamo spesso in termini di byte (8 bit). Un byte può rappresentare numeri interi da 0 a 255. È comune lavorare con valori di byte in esadecimale, 0x00 - 0xFF.
I valori letterali binari si definiscono con la forma speciale per i bitstring <<>>. Quando definiamo un valore letterale binario, possiamo usare valori letterali interi e stringhe. I valori interi maggiori di 255 vanno in overflow e vengono usati solo gli ultimi 8 bit dell'intero. Per impostazione predefinita, al valore viene applicato il modificatore ::binary. Possiamo concatenare i binari con l'operatore <>/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 null-byte è un altro nome per <<0>>.
Il pattern matching è persino esteso ai binari: possiamo applicarlo a una porzione di dati binari, proprio come faremmo con un array.
# Ignore the first 8 bytes, match and bind the remaining to `body`
<<_::binary-size(8), body::binary>>
Come per gli altri tipi di pattern matching, possiamo usarlo nelle firme delle funzioni per scegliere tra più clausole di funzione.
Hai lavorato a un progetto che consente agli utenti di caricare file sul server per condividerli con altri utenti. Ti è stato chiesto di scrivere una funzione per verificare che un caricamento corrisponda al suo tipo di media. Fai qualche ricerca e scopri che i primi byte di un file sono generalmente unici per quel tipo di file, una sorta di firma.
Usa la tabella seguente come riferimento:
| Tipo di file | Estensione comune | Tipo di media | «firma» binaria |
|---|---|---|---|
| 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 |
Implementa la funzione type_from_extension/1. Deve accettare un'estensione di file (stringa) e restituire il tipo di media (stringa) oppure nil se l'estensione non corrisponde a quelle previste.
FileSniffer.type_from_extension("exe")
# => "application/octet-stream"
FileSniffer.type_from_extension("txt")
# => nil
Implementa la funzione type_from_binary/1. Deve accettare un file (binario) e restituire il tipo di media (stringa) oppure nil se la firma del file non corrisponde a quelle previste.
file = File.read!("application.exe")
FileSniffer.type_from_binary(file)
# => "application/octet-stream"
file = File.read!("example.txt")
FileSniffer.type_from_binary(file)
# => nil
Non preoccuparti di leggere il file come binario. Dai per scontato che sia già stato fatto e che i test lo forniscano come argomento.
Implementa la funzione verify/2. Deve accettare un file (binario) e un'estensione (stringa) e restituire una tupla :ok o :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."}
Iscriviti a Exercism per imparare e padroneggiare Elixir con 58 concetti168 esercizi e il mentoring di persone reali, tutto gratis.