Encoder et décoder une chaîne
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 les chaînes de caractères des chaînes d’octets, et encode_base64 veut des octets : le texte passe donc d’abord par Encode::encode('UTF-8', ...). Une chaîne contenant des caractères au-delà de U+00FF provoque l’erreur fatale Wide character in subroutine entry ; une chaîne qui ne contient que des caractères Latin-1 comme é est encodée sans avertissement en Latin-1, pas en UTF-8.
L’argument '' est expliqué dans la section suivante.
L’argument de fin de ligne
encode_base64($bytes, $eol) coupe la sortie à 76 caractères et ajoute $eol à chaque ligne, y compris la dernière. La valeur par défaut est "\n" :
print encode_base64('hi'); # "aGk=\n"
print encode_base64('x' x 60);
# eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
# eHh4
C’est correct pour le corps des e-mails MIME, et faux pour presque tout le reste : en-têtes HTTP, JSON, URL et champs de base de données. Passez '' pour une sortie sur une seule ligne, ou "\r\n" pour des fins de ligne strictement conformes à la RFC 2045.
Pour un en-tête HTTP, cela donne :
print "Authorization: Basic ", encode_base64('user:pass', ''), "\n";
# Authorization: Basic dXNlcjpwYXNz
Le décodage ne se soucie pas des fins de ligne : decode_base64("aGVs\r\nbG8K") renvoie "hello\n", si bien que les corps MIME avec \n ou \r\n se décodent directement.
Base64 URL-safe
MIME::Base64 exporte encode_base64url et decode_base64url sur demande. L’encodeur supprime le remplissage et le décodeur accepte une entrée qui n’en a pas :
use MIME::Base64 qw(encode_base64url decode_base64url);
print encode_base64url('<<???>>'), "\n"; # PDw_Pz8-Pg
print decode_base64url('PDw_Pz8-Pg'), "\n"; # <<???>>
Pour une sortie URL-safe qui garde le remplissage, convertissez la forme standard avec tr :
my $padded = encode_base64('<<???>>', '') =~ tr{+/}{-_}r; # PDw_Pz8-Pg==
L’option /r (Perl 5.14+) renvoie la copie modifiée. Base64 ou Base64URL explique quels systèmes attendent quelle variante.
Encoder et décoder un fichier
Ouvrez les fichiers en mode :raw pour qu’aucune couche ne convertisse les sauts de ligne ou les encodages :
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;
Gros fichiers par blocs
Lisez par multiples de 57 octets. 57 octets s’encodent en exactement 76 caractères : chaque bloc produit des lignes MIME complètes, et la sortie concaténée est identique à l’encodage du fichier entier.
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;
Le résultat redonnait le fichier d’origine de 300 Ko une fois décodé avec base64 -d. Pour une sortie sur une seule ligne, utilisez encode_base64($buf, '') avec n’importe quelle taille de bloc divisible par 3. Le module sait aussi donner les tailles sans encoder : MIME::Base64::encoded_base64_length('hello') renvoie 9 (saut de ligne compris) et decoded_base64_length('aGVsbG8=') renvoie 5. Le guide du surcoût de taille donne la formule.
En ligne de commande, le module permet deux one-liners bons à connaître :
perl -MMIME::Base64 -0777 -ne 'print encode_base64($_, "")' < file.bin
perl -MMIME::Base64 -0777 -ne 'print decode_base64($_)' < file.b64 > file.bin
Validation : decode_base64 ne se plaint jamais
decode_base64 est tolérant par conception. Il écarte tout ce qui est hors de l’alphabet, s’arrête au remplissage et renvoie ce qu’il a :
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 "<<???>>"
Le dernier cas est le plus dangereux : une entrée URL-safe passée au décodeur standard produit en silence des octets erronés. Utilisez decode_base64url pour les jetons. Pour une entrée non fiable, vérifiez l’alphabet et la longueur, puis confirmez l’aller-retour :
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
Le validateur Base64 effectue les mêmes contrôles dans le navigateur et indique quel caractère est fautif. Pour les chaînes abîmées issues de logs, essayez Réparer Base64 ; les erreurs Base64 courantes expliquent ce qui les cause le plus souvent.
Pièges
- Le saut de ligne final dans les en-têtes.
"Authorization: Basic " . encode_base64("$user:$pass")se termine par\n, ce qui casse la requête. Passez''. - Le double encodage. Sous
use utf8avec une couche de sortie:encoding(UTF-8), afficher des octets décodés sansdecode('UTF-8', ...)produit du mojibake commehéllo. - Considérer Base64 comme un secret. N’importe qui peut l’inverser. Voir encodage ou chiffrement.
FAQ
Pourquoi encode_base64 ajoute-t-il un saut de ligne ?
Le deuxième argument est la fin de ligne, et il vaut "\n" par défaut. La sortie est coupée à 76 caractères et se termine toujours par cette chaîne. Passez '' pour obtenir une seule ligne.
Que signifie « Wide character in subroutine entry » ?
Vous avez passé à encode_base64, qui n’accepte que des octets, une chaîne de caractères contenant des points de code supérieurs à 255. Encodez d’abord le texte : encode_base64(encode('UTF-8', $text), '').
decode_base64 valide-t-il son entrée ?
Non. Il ignore les caractères hors de l’alphabet et renvoie tout ce qu’il parvient à décoder : decode_base64('aGk=!!') donne hi. Vérifiez la chaîne avec une expression régulière avant de la décoder si l’entrée n’est pas fiable.