Elixir ofrece una sintaxis elegante para trabajar con datos binarios, como hemos visto con la forma especial <<>> que se proporciona para trabajar con cadenas de bits.
El tipo binario es una especialización del tipo cadena de bits. Mientras que las cadenas de bits pueden tener cualquier longitud (cualquier cantidad de bits), los binarios son aquellos en los que la cantidad de bits es divisible por 8. Es decir, cuando trabajamos con binarios, a menudo pensamos en términos de bytes (8 bits). Un byte puede representar números enteros desde 0 hasta 255. Es común trabajar con valores de byte en hexadecimal, 0x00 - 0xFF.
Los literales binarios se definen usando la forma especial de cadena de bits <<>>. Al definir un literal binario, podemos usar literales de enteros y de string. Los valores enteros mayores que 255 se desbordarán y solo se usarán los últimos 8 bits del entero. De forma predeterminada, el modificador ::binary se aplica al valor. Podemos concatenar binarios con el operador <>/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 byte nulo es otro nombre para <<0>>.
La coincidencia de patrones se extiende incluso a los binarios, y podemos aplicar coincidencia de patrones a una porción de datos binarios, tal como haríamos con una lista.
# Ignore the first 8 bytes, match and bind the remaining to `body`
<<_::binary-size(8), body::binary>>
Al igual que con otros tipos de coincidencia de patrones, podemos usar esto en las firmas de funciones para hacer coincidir al seleccionar entre múltiples cláusulas de función.
Has estado trabajando en un proyecto que permite a los usuarios subir archivos al servidor para compartirlos con otros usuarios. Te han encargado escribir una función para verificar que una carga coincida con su tipo de medio. Investigas un poco y descubres que los primeros bytes de un archivo suelen ser únicos de ese tipo de archivo, lo que le da una especie de firma.
Usa la siguiente tabla como referencia:
| Tipo de archivo | Extensión común | Tipo de medio | «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 función type_from_extension/1. Debe recibir una extensión de archivo (string) y devolver el tipo de medio (string) o nil si la extensión no coincide con las esperadas.
FileSniffer.type_from_extension("exe")
# => "application/octet-stream"
FileSniffer.type_from_extension("txt")
# => nil
Implementa la función type_from_binary/1. Debe recibir un archivo (binario) y devolver el tipo de medio (string) o nil si la firma del archivo no coincide con las esperadas.
file = File.read!("application.exe")
FileSniffer.type_from_binary(file)
# => "application/octet-stream"
file = File.read!("example.txt")
FileSniffer.type_from_binary(file)
# => nil
No te preocupes por leer el archivo como binario. Asume que eso ya se hizo por ti y que las pruebas lo proporcionan como argumento.
Implementa la función verify/2. Debe recibir un archivo (binario) y una extensión (string) y devolver 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."}
Regístrate en Exercism para aprender y dominar Elixir con 58 conceptos168 ejercicios y mentoría humana real, todo gratis.