Codificar y decodificar una cadena
use strict;
use warnings;
use utf8;
use MIME::Base64 qw(encode_base64 decode_base64);
use Encode qw(encode decode);
my $enc = encode_base64(encode('UTF-8', 'héllo wörld'), '');
print "$enc\n"; # aMOpbGxvIHfDtnJsZA==
print decode('UTF-8', decode_base64($enc)), "\n"; # héllo wörld
Perl distingue entre cadenas de caracteres y cadenas de bytes, y encode_base64 quiere bytes, así que el texto pasa primero por Encode::encode('UTF-8', ...). Una cadena con caracteres por encima de U+00FF muere con Wide character in subroutine entry; una que solo tenga caracteres Latin-1, como é, se codifica sin avisar como Latin-1, no como UTF-8.
El argumento '' se explica en la siguiente sección.
El argumento de final de línea
encode_base64($bytes, $eol) corta la salida a 76 caracteres y añade $eol a cada línea, incluida la última. El valor por defecto es "\n":
print encode_base64('hi'); # "aGk=\n"
print encode_base64('x' x 60);
# eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
# eHh4
Eso es correcto para los cuerpos de correo MIME e incorrecto para casi todo lo demás: cabeceras HTTP, JSON, URL y campos de base de datos. Pasa '' para obtener una sola línea, o "\r\n" para finales de línea estrictos según el RFC 2045.
Para una cabecera HTTP, eso significa:
print "Authorization: Basic ", encode_base64('user:pass', ''), "\n";
# Authorization: Basic dXNlcjpwYXNz
Al decodificar, los finales de línea dan igual: decode_base64("aGVs\r\nbG8K") devuelve "hello\n", así que los cuerpos MIME con \n o con \r\n se decodifican directamente.
Base64 URL-safe
MIME::Base64 exporta encode_base64url y decode_base64url si los pides. El codificador quita el relleno y el decodificador acepta entradas sin él:
use MIME::Base64 qw(encode_base64url decode_base64url);
print encode_base64url('<<???>>'), "\n"; # PDw_Pz8-Pg
print decode_base64url('PDw_Pz8-Pg'), "\n"; # <<???>>
Para una salida URL-safe que conserve el relleno, traduce la forma estándar con tr:
my $padded = encode_base64('<<???>>', '') =~ tr{+/}{-_}r; # PDw_Pz8-Pg==
El modificador /r (Perl 5.14+) devuelve la copia modificada. Base64 frente a Base64URL explica qué sistemas esperan cada variante.
Codificar y decodificar un archivo
Abre los archivos en modo :raw para que ninguna capa traduzca saltos de línea ni codificaciones:
open my $in, '<:raw', 'hello.txt' or die $!;
my $data = do { local $/; <$in> };
close $in;
print encode_base64($data, ''), "\n"; # aGVsbG8K
open my $out, '>:raw', 'copy.txt' or die $!;
print {$out} decode_base64('aGVsbG8K');
close $out;
Archivos grandes por bloques
Lee en múltiplos de 57 bytes. 57 bytes se codifican en exactamente 76 caracteres, así que cada bloque produce líneas MIME completas y la salida concatenada es idéntica a codificar el archivo entero:
open my $in, '<:raw', 'big.bin' or die $!;
open my $out, '>:raw', 'big.b64' or die $!;
while (read($in, my $buf, 57 * 1024)) {
print {$out} encode_base64($buf);
}
close $in;
close $out;
El resultado se decodificó con base64 -d y devolvió el archivo original de 300 KB. Para una salida de una sola línea, usa encode_base64($buf, '') con cualquier tamaño de bloque divisible entre 3. El módulo también te da los tamaños sin codificar: MIME::Base64::encoded_base64_length('hello') devuelve 9 (incluido el salto de línea) y decoded_base64_length('aGVsbG8=') devuelve 5. La guía del aumento de tamaño tiene la fórmula.
Desde la línea de comandos, el módulo permite dos one-liners que conviene conocer:
perl -MMIME::Base64 -0777 -ne 'print encode_base64($_, "")' < file.bin
perl -MMIME::Base64 -0777 -ne 'print decode_base64($_)' < file.b64 > file.bin
Validación: decode_base64 nunca se queja
decode_base64 es tolerante por diseño. Descarta todo lo que está fuera del alfabeto, se detiene en el relleno y devuelve lo que tenga:
decode_base64('aGk=!!'); # "hi"
decode_base64('aGk'); # "hi" (missing padding is fine)
decode_base64('aGl='); # "hi" (non-zero padding bits ignored)
decode_base64('a'); # "" (too short, no warning)
decode_base64('PDw_Pz8-Pg=='); # "<<\x0f\xcf\xc3\xe0", not "<<???>>"
El último es el caso peligroso: una entrada URL-safe pasada al decodificador estándar produce bytes incorrectos sin avisar. Usa decode_base64url para los tokens. Con entradas que no son de confianza, comprueba el alfabeto y la longitud, y después confirma la ida y vuelta:
sub is_base64 {
my ($s) = @_;
return 0 unless $s =~ m{\A[A-Za-z0-9+/]*={0,2}\z} && length($s) % 4 == 0;
return encode_base64(decode_base64($s), '') eq $s ? 1 : 0;
}
# is_base64('aGk=') = 1, 'aGk' = 0, 'aGl=' = 0, 'aGk=!!' = 0
El validador Base64 hace las mismas comprobaciones en el navegador y te dice qué carácter falla. Para cadenas rotas sacadas de logs, prueba Reparar Base64; errores habituales de Base64 explica qué suele causarlas.
Trampas
- Salto de línea final en las cabeceras.
"Authorization: Basic " . encode_base64("$user:$pass")termina en\n, lo que rompe la petición. Pasa''. - Doble codificación. Con
use utf8y una capa de salida:encoding(UTF-8), imprimir bytes decodificados sindecode('UTF-8', ...)muestra mojibake comohéllo. - Tratar Base64 como un secreto. Cualquiera puede revertirlo. Consulta codificación frente a cifrado.
Preguntas frecuentes
¿Por qué encode_base64 añade un salto de línea?
El segundo argumento es el final de línea, y por defecto vale "\n". La salida se corta a 76 caracteres y siempre termina con esa cadena. Pasa '' para obtener una sola línea.
¿Qué significa «Wide character in subroutine entry»?
Has pasado a encode_base64 una cadena de caracteres con puntos de código por encima de 255, y la función solo acepta bytes. Codifica antes el texto: encode_base64(encode('UTF-8', $text), '').
¿decode_base64 valida la entrada?
No. Ignora los caracteres que están fuera del alfabeto y devuelve lo que consiga decodificar, así que decode_base64('aGk=!!') da hi. Si la entrada no es de confianza, comprueba la cadena con una expresión regular antes de decodificar.