Base64-Kodierung in Perl: Ein vollständiger Leitfaden
Sie haben Daten, die einen Kanal überleben müssen, der sie nicht mag. Ein binärer Blob, der in ein JSON-Feld muss. Ein Bild, das in einem HTML-Tag leben muss. Ein Zertifikat, das in eine Konfigurationsdatei gehört. Ein Token, das durch URLs, Header und Query-Strings reisen wird. Das ist der Alltag der Base64-Kodierung: Sie schreibt jeweils drei Bytes rohe Daten in vier Zeichen aus einem 64-Buchstaben-Alphabet um, mit ein oder zwei =-Zeichen, die den Schwanz abschließen, sodass das Ergebnis ein gewöhnlicher Text ist, den alles tragen kann, typischerweise etwa 33 Prozent länger als das, womit Sie angefangen haben. Die Startseite dieser Site erklärt das Format in voller Tiefe, also konzentriert sich dieser Artikel darauf, was Perl Ihnen gibt, was es still für Sie entscheidet und wo die Fallen sind.
Zuerst die gute Nachricht: encode_base64 lebt seit 2002 im Kern von Perl, es ist in C implementiert und bequem schnell. Der interessante Teil ist, dass die Funktion Ansichten hat. Sie bricht ihre Ausgabe bei 76 Zeichen um, sie hängt ein abschließendes Zeilenende an, und sie wird keine Unicode-Zeichen kodieren, die Sie nicht zuerst in Bytes umgewandelt haben: Über dem Latin-1-Bereich stirbt sie sofort, und unterhalb dieser Linie nimmt sie still Latin-1-Bytes an. Dieser Leitfaden geht jeden Geschmack von Base64 durch, den ein Perl-Entwickler tatsächlich produziert: den Einzeiler, den MIME-umwickelten E-Mail-Body, den PEM-umwickelten Schlüssel, den URL-sicheren Token und die Streaming-Version für Dateien, die zu groß sind, um sie im Speicher zu halten.
Eine Funktion, ein verstecktes Zeilenende
Die komplette API, genau so, wie die moderne Dokumentation sie präsentiert:
encode_base64( $bytes )
encode_base64( $bytes, $eol )
Lesen Sie das noch einmal. Zwei Argumente, eines davon optional, ein Rückgabewert, keine Flags. Das optionale zweite Argument ist die Sequenz des Zeilenendes, und sie ist standardmäßig ein gewöhnliches Zeilenende, was bedeutet, dass der unschuldigste Aufruf in Perl umgebrochene, mit Zeilenende abgeschlossene Ausgabe produziert:
use MIME::Base64 qw(encode_base64);
my $wrapped = encode_base64("Aladdin:open sesame");
print length($wrapped), "\n"; # 29: die 28 Zeichen plus ein Zeilenende
my $single = encode_base64("Aladdin:open sesame", "");
print length($single), "\n"; # 28: übergeben Sie einen leeren String für keinen Umbruch
Die Ausgabegröße folgt einem festen Muster, das Sie vor dem Aufruf vorhersagen können:
| Eingabe-Bytes | Ausgabe-Zeichen | Padding |
|---|---|---|
| 0 | 0 | keins |
| 1 | 4 | zwei = |
| 2 | 4 | ein = |
| 3 | 4 | keins |
| 100.000 | 133.336 | keins, auf einer einzigen Zeile |
| 3.000.000 | 4.000.000 | keins |
Das Muster ist vier Zeichen für jede vollständige Gruppe von drei Bytes, plus eine letzte Teilgruppe, die mit ein oder zwei =-Zeichen aufgefüllt wird. Eine Konsequenz, die zu wissen lohnt sich: Ein Byte und drei Bytes produzieren beide vier Zeichen, also versteckt die kodierte Länge die genaue Eingabegröße. Und wenn Sie die Größe brauchen, ohne die Arbeit zu machen, hat das Modul eine Längenfunktion seit 3.10 aus 2010. Sie wird nur standardmäßig nicht exportiert, also rufen Sie sie über den Paketnamen auf:
use MIME::Base64 ();
my $with_wrap = MIME::Base64::encoded_base64_length($bytes); # 76-Zeichen-Zeilen, Standard-eol
my $single_line = MIME::Base64::encoded_base64_length($bytes, ""); # kein Umbruch
my $mime_body = MIME::Base64::encoded_base64_length($bytes, "\r\n");
Es gibt noch eine Regel zu memorieren, denn es ist der einzige Weg, auf dem der Encoder je schreit: Wenn der String, den Sie ihm übergeben, Zeichen mit einem Code über 255 enthält, stirbt encode_base64 mit dem Breiten Zeichen am Subroutine-Einstieg. Unterhalb dieser Linie ist der Fehlschlag leiser: Zeichen bis 255 werden still auf ihre Latin-1-Bytes herabgestuft, also kodiert ein String mit accented Zeichen, der die Umwandlung übersprungen hat, als Latin-1 statt UTF-8, und niemand sagt es Ihnen. Die Base64-Kodierung ist nur für Einbyte-Zeichen definiert, und Perl 5.8 und besser erlauben erweiterte Zeichen in Strings, also ist die Umwandlung eine Entscheidung, die Sie bewusst treffen, mit Encode, im nächsten Abschnitt.
Text oder Bytes? Der Schritt, den die Funktion nicht tun kann
Perl-Strings tragen ein stilles Flag, das sagt, ob sie Zeichen oder Bytes halten, und Base64 lebt auf der Bytes-Seite dieser Linie. Wenn Ihr Text eine Zeichenkette ist - und das ist der Fall, wenn er aus einem JSON-Parser, einer Vorlage oder einem Literal mit accented Buchstaben in einer UTF-8-Quelldatei kommt -, verweigert der Encoder, zu raten, welche Bytes Sie für Zeichen über dem Latin-1-Bereich gemeint haben, und er sagt es Ihnen; unterhalb des Bereichs ratet er still Latin-1. Die Lösung ist in beiden Fällen dieselbe, und es ist eine Core-Funktion aus dem Encode-Modul:
use MIME::Base64 qw(encode_base64);
use Encode qw(encode);
my $chars = "H\x{eb}llo W\x{f6}rld"; # eine Zeichenkette
my $utf8 = encode("UTF-8", $chars); # jetzt: Bytes
my $b64 = encode_base64($utf8, "");
print $b64, "\n"; # SMOrbGxvIFfDtnJsZA==
Der encode-Aufruf ist der ganze Tanz: Wählen Sie die Byte-Darstellung, und UTF-8 für alles Moderne, wandeln Sie die Zeichen in diese Bytes um, und erst dann übergeben Sie die Bytes dem Encoder. Für legacy Westtext, der als Windows 1252 angekommen ist, ist die Umwandlung dieselbe Funktion mit einem anderen Namen, encode("Windows-1252", $legacy), die Ihnen die ursprüngliche Einbyte-Form in die Hand gibt. Das Encode-Modul ist Core, also kostet das alles nichts.
Jetzt die Falle. Wenn die Bytes, die Sie haben, bereits UTF-8 sind und Sie sie noch einmal durch encode("UTF-8", ...) jagen, in dem Glauben, Sie würden sie UTF-8 machen, bekommen Sie keine Kopie: Sie bekommen eine doppelte Kodierung, bei der jeder accentierte Buchstabe zu zwei eigenen Zeichen aufbläht. Das klassische Symptom ist Text, der früher Hëllo las und jetzt Hëllo liest, und jeder Decoder im Internet wird das getreu für Sie dekodieren:
use Encode qw(encode decode);
my $right = encode("UTF-8", "H\x{eb}llo");
my $wrong = encode("UTF-8", $right); # die Bytes als Zeichen neu kodieren
print decode("UTF-8", $right), "\n"; # Hëllo
print decode("UTF-8", $wrong), "\n"; # Hëllo
Die Daumenregel, die es verhindert: Bytes werden genau einmal kodiert, und utf8::is_utf8() zeigt Ihnen, auf welcher Seite der Linie ein String steht. Wenn das Flag gesetzt ist, halten Sie Zeichen in den Händen, und der encode()-Aufruf ist der richtige Zug; wenn es nicht gesetzt ist, halten Sie Bytes in den Händen, und Sie sind bereit für Base64.
Zeilenumbruch: Drei Dialekte, eine Regel
Da die Standardausgabe umgebrochen und mit Zeilenende abgeschlossen ist, ist die erste Entscheidung für jeden Kodierjob ein Zielproblem: Wo wird dieser String leben? Die vereinheitlichende Tatsache ist, dass Decoder Zeilenumbrüche komplett ignorieren: RFC 2045 sagt Decoder-Software, alle Zeilenumbrüche und Zeichen außerhalb des Alphabets zu ignorieren, also ist der Umbruch eine Höflichkeit gegenüber zeilenbasierten Tools und Menschen, kein semantischer Unterschied. Die drei Antworten in der Praxis:
Keine Zeilenumbrüche. Die Funktionsausgabe mit deaktiviertem Umbruch, genau eine Zeile. Das ist es, was Sie für URLs, JSON-Payloads, Header, Datenbankwerte und alles andere wollen, wo ein Zeilenumbruch ein Bug wäre. Das ist auch das, was die meisten Leute meinen, wenn sie nach einfach nur Base64 fragen:
my $single = encode_base64($bytes, "");
MIME: 76 Zeichen plus CRLF. Die E-Mail-Konvention aus RFC 2045: kodierte Zeilen dürfen 76 Zeichen nicht überschreiten, und die MIME-Welt spricht CRLF. Den erledigt das Modul selbst, mit dem zweiten Argument:
my $mime_body = encode_base64($bytes, "\r\n");
PEM: 64 Zeichen plus LF. Schlüssel und Zertifikate verwenden die ältere Konvention mit kürzeren 64-Zeichen-Zeilen, und das Modul kann diese Breite nicht von selbst produzieren, also füllt ein vierzeiliger Helfer die Lücke:
sub wrap_lines {
my ($text, $width) = @_;
return join "\n", $text =~ /(.{1,$width})/g;
}
my $pem = "-----BEGIN CERTIFICATE-----\n"
. wrap_lines(encode_base64($der, ""), 64) . "\n"
. "-----END CERTIFICATE-----\n";
Derselbe Helfer dient auch den anderen 64-Zeichen-Dialekten - der PKIX-Textkodierung aus RFC 7468 und der OpenPGP-Panzerung, deren Datenzeilen 64 Zeichen breit sind und deren abschließende CRC24-Prüfsummen-Zeile GnuPG hinzufügt und nicht Sie. Ein Stolperstein gilt für alle: encode_base64 hängt das Zeilenende ganz ans Ende des Ergebnisses, auch wenn die letzte Zeile ihre Breite exakt füllt. Wenn ein nachgelagerter Konsument über diese abschließende Leerzeile stolpert, räumt ein rtrim-Aufruf auf dem Ergebnis auf.
URL-sicheres Base64: Das - und _ Alphabet
Das Standardalphabet enthält + und /, und beide sind Ärger außerhalb einer Textdatei: Ein + in einem formular-kodiertem Query-String wird zu einem Leerzeichen, bevor Ihre Anwendung es je sieht, und / ist ein Pfadtrenner in URLs. Dateinamen und Tokens haben ihre eigenen Beschwerden. RFC 4648, Abschnitt 5, löst das mit dem für URLs und Dateinamen sicheren Alphabet, in dem + zu - wird, / zu _, und das =-Padding am Ende meist weggelassen wird. Der RFC besteht darauf, dass dies nicht als dasselbe wie die base64-Kodierung betrachtet werden sollte, also behandeln Sie es als eigenes Format, allgemein base64url genannt. Perl produziert es seit Version 3.11 aus 2010 in einem Aufruf:
use MIME::Base64 qw(encode_base64url);
my $seg = encode_base64url("sunset-42");
print $seg, "\n"; # c3Vuc2V0LTQy: kein Padding, kein Zeilenende
Dieser eine Aufruf erledigt alle drei Änderungen: den Alphabet-Tausch, kein Padding, keine Zeilenumbrüche. Wenn Sie bereits Standard-Base64 in den Händen halten und das Ziel den URL-sicheren Dialekt will, wandeln zwei Zeichenkettenoperationen es an Ort und Stelle um:
sub to_urlsafe {
my ($b64) = @_;
$b64 =~ tr{+/}{-_};
return $b64 =~ s/=+\z//r;
}
my $seg = to_urlsafe(encode_base64($bytes, ""));
Wann es verwenden: JSON Web Token-Teile, OAuth-State- und Nonce-Parameter, API-IDs, die Sie in URL-Pfade legen, und undurchsichtige Schlüssel, die eine Adressleiste oder einen Dateinamen überleben müssen, wo CPANs Data::UUID::Base64URLSafe genau dafür existiert. Wann es nicht verwenden: E-Mail-Bodies, PEM-Panzerung und überall, wo am anderen Ende ein Standardalphabet-Konsument sitzt, denn - und _ sind nicht in ihrem Wortschatz. Und mischen Sie die beiden Alphabete nicht still: Ein URL-sicher kodierter Wert muss URL-sicher dekodiert werden, überall, für immer. Auf Perls älter als 3.11 liefert das eigenständige MIME::Base64::URLSafe-Modul aus 2006, ein Port von Pythons urlsafe-Codec, urlsafe_b64encode; auf allem Modernen ist die eingebaute Funktion das richtige Werkzeug.
Einen JWT bauen: Jeder Teil von Hand
JSON Web Tokens sind das Flaggschiff unter den Base64-Verbrauchern in modernen APIs, und sie verwenden den URL-sicheren, ohne-Padding-Dialekt aus dem Abschnitt oben. Laut RFC 7515 ist ein kompakter JWT drei durch Punkte getrennte base64url-Teile: der geschützte Header, der Payload und die Signatur. Einen von Hand zu bauen ist eine angenehme Art, jedes bewegliche Teil zu sehen:
use MIME::Base64 qw(encode_base64url);
use JSON::PP qw(encode_json);
use Digest::SHA qw(hmac_sha256);
my $secret = "correct-horse-battery-staple";
my $head = encode_base64url(encode_json({ alg => "HS256", typ => "JWT" }));
my $claims = encode_base64url(encode_json({ sub => "homer", role => "admin" }));
my $sig = encode_base64url(hmac_sha256("$head.$claims", $secret));
my $jwt = "$head.$claims.$sig";
print $jwt, "\n"; # ein kompakter HS256-Token; die Schlüsselreihenfolge in jedem JSON-Teil variiert von Lauf zu Lauf
Drei Details, die es zu bemerken lohnt. Erstens gibt encode_json aus dem Core-Modul JSON::PP kompakte UTF-8-Bytes ohne Leerraum aus, genau das, was die JOSE-Spezifikationen innerhalb eines Tokens wollen. Zweitens kann den Payload jeder lesen, und das ist Absicht: Ein JWT ist ein signierter Scheck, nicht ein Geheimnis, also gehören nie vertrauliche Werte in die Claims. Drittens ist die Signatur die base64url-Kodierung roher HMAC-Bytes, deshalb geht hmac_sha256 direkt in den Encoder, ohne jegliches Hex-Formatting.
Für die Produktion rollen Sie die Signierung nicht von Hand. Das CPAN-Modul Crypt::JWT, das auf CryptX aufbaut, implementiert JWS und JWE mit dem vollen Algorithmus-Satz:
use Crypt::JWT qw(encode_jwt);
my $jwt = encode_jwt(
payload => { sub => "homer", role => "admin" },
alg => "HS256",
key => $secret,
);
Und auf der Empfängerseite: Fixieren Sie den Algorithmus mit accepted_alg, damit ein Angreifer den Token nicht auf eine schwächere Variante umschalten kann: decode_jwt(token => $jwt, key => $secret, accepted_alg => "HS256") verifiziert die Signatur und croakt bei Fehlschlag. Von Hand rollen ist für das Verständnis gut; eine Bibliothek ist für Geld gut.
HTTP: Auth-Header, Data-URIs und der WebSocket-Handshake
Der Authorization: Basic-Header ist der älteste lebende Anwendungsfall: Benutzername und Passwort, verbunden durch einen Doppelpunkt, als eine Zeile kodiert, mit dem Schema-Wort vorangestellt. Der leere String als zweites Argument trägt hier Last, denn ein abschließendes Zeilenende innerhalb eines Header-Feldes ist ein Bug:
use MIME::Base64 qw(encode_base64);
my $user = "alice";
my $pass = "s3cr3t";
my $header = "Basic " . encode_base64("$user:$pass", "");
print $header, "\n"; # Basic YWxpY2U6czNjcjN0
Data-URIs aus RFC 2397 sind dieselbe Idee, angewandt auf Bilder: Der Payload sitzt direkt in der URL, also ist keine zweite Anfrage nötig, um ihn abzuholen. Binäre Medien verwenden das ;base64-Flag, also ist der Payload genau das, was encode_base64 mit deaktiviertem Umbruch produziert hat:
my $uri = "data:" . $mime_type . ";base64," . encode_base64($bytes, "");
Die Abwägungen sind aber real. Der kodierte Payload ist etwa 33 Prozent größer als die Datei, was das HTML-Dokument selbst größer macht. Browser cachen eine Data-URI nicht so, wie sie eine Datei-URL cachen - es gibt keinen separaten Fetch zum Cachen, also verschickt jeder Seitenaufruf die Bytes erneut als Teil des Dokuments, und der RFC selbst sagt, Data-URIs seien nur für kurze Werte nützlich. Verwenden Sie sie für Avatare, Icons und kleine Inline-Grafiken; für alles andere echte Dateien. Es gibt eine dritte HTTP-Ecke, die Base64 still verwendet: den WebSocket-Handshake aus RFC 6455, wo der Client einen Sec-WebSocket-Key-Header sendet, der das Base64 von sechzehn zufälligen Bytes ist. Frameworks wie Mojolicious tun es für Sie, aber wenn Sie es je auf der Leitung sehen, wissen Sie jetzt, was es ist:
use MIME::Base64 qw(encode_base64);
my $key = encode_base64(pack("C16", map { int(rand 256) } 1 .. 16), "");
print length($key), "\n"; # 24: sechzehn zufällige Bytes, aufgefüllt zur Vier-Zeichen-Gruppe
Dateien: Slurps, 57-Byte-Chunks und die CLI
Der direkteste Kodierjob: Eine Datei wird zu Text. Perl-Strings sind Bytes, also gibt es keinen Binärmodus, den man suchen müsste - der :raw-Layer ist das Ganze. Roh öffnen, lesen, kodieren, schreiben:
use MIME::Base64 qw(encode_base64);
open my $in, "<:raw", $ARGV[0] or die $!;
local $/;
my $bytes = <$in>;
close $in;
open my $out, ">:raw", $ARGV[1] or die $!;
print {$out} encode_base64($bytes);
close $out;
Die :raw-Layer sind wichtig. Ohne sie würde Perl versuchen, die Bytes auf dem Hin- und Rückweg als Plattformtext zu interpretieren, und auf einem System mit anderer Standard-Kodierung ist das genau die Beschädigung, die Sie nicht sehen, bis die Datei woanders geöffnet wird. Und denken Sie an die Größenrechnung, wenn Sie Speicher planen: Ein 500-kB-Bild wird zu einer 670-kB-Textdatei, und ein 1-GB-Video wird 1,33 GB.
Für Dateien, die zu groß sind, um sie im Speicher zu halten, gibt die Dokumentation des Moduls selbst Ihnen die Regel: Kodieren Sie in Chunks, die ein Vielfaches von 57 Bytes sind, denn 57 Bytes Daten füllen genau eine 76-Zeichen-Zeile, 76 ergibt sich aus 57 mal 4 geteilt durch 3. Chunken Sie an dieser Grenze, und Sie bekommen nie Padding in der Mitte des Streams:
use MIME::Base64 qw(encode_base64);
open my $in, "<:raw", $ARGV[0] or die $!;
while (read($in, my $buf, 57 * 10)) {
print encode_base64($buf);
}
close $in;
Jeder Chunk landet exakt auf Zeilengrenzen, der letzte, möglicherweise kürzere, Chunk trägt das finale Padding, und das Ergebnis ist Byte für Byte dasselbe wie das Slurpen der ganzen Datei und das Einmal-Kodieren, nur mit konstantem Speicherverbrauch. Und wenn Sie gar kein Skript brauchen, deckt der Einzeiler es ab: -0777 slurpt die Eingabe, und das leere String-Argument hält die Ausgabe auf einer Zeile:
perl -MMIME::Base64 -0777 -ne 'print encode_base64($_, "")' < file > file.b64
E-Mail: MIME-Bodies und Anhänge
E-Mail ist der Ort, an dem Base64 seinen Namen verdient hat. Der MIME-Standard sagt, Daten, die nicht sicher als roher Text reisen können, sollen mit Content-Transfer-Encoding: base64 gesendet werden, in Zeilen von nicht mehr als 76 Zeichen. Wenn Sie E-Mail mit MIME::Lite bauen, ist die ganze Sache ein Argument, und das Modul erledigt die Kodierung, den Umbruch und den Header für Sie:
use MIME::Lite;
my $mime = MIME::Lite->new(
From => 'me@example.com',
To => 'you@example.com',
Subject => 'A file',
Type => 'text/plain',
Data => 'The body text.',
);
$mime->attach(
Type => 'application/octet-stream',
Data => $bytes,
Encoding => 'base64',
Filename => 'hello.txt',
);
Das Encoding-Argument ist der Auslöser: MIME::Lite base64-kodiert den Anhang in 76-Zeichen-Zeilen (mit dem Standard des Moduls, dem nackten Zeilenende; der Mail-Transport ist es, der sie in CRLF verwandelt) und stempelt das Teil mit dem passenden Content-Transfer-Encoding-Header. Email::MIME nimmt dieselbe Haltung ein und base64-kodiert jeden Anhang, den Sie ihm als rohen Daten-String übergeben (seine Doku: "alle auf diese Weise erzeugten Teile werden mit base64 kodiert, nur für den Fall"). Wenn Sie eine rohe MIME-Nachricht von Hand zusammenbauen, ist das Äquivalent die zwei Zeilen aus dem Umbruch-Abschnitt, encode_base64($bytes, "\r\n") plus die Header-Zeile, und das ist die gesamte Protokoll-Seite der Geschichte.
Datenbanken, Konfiguration und Umgebungsvariablen
Datenbanken: Binärdaten reisen oft in einer TEXT-Spalte als Base64, weil die Spalte nicht zusichern kann, beliebige Bytes unverändert durchzulassen. Speichern Sie die Einzeilen-Form, nie die umgebrochene, sonst liefert Ihr nächstes SELECT einen String mit Zeilenenden in der Mitte des Werts zurück:
use MIME::Base64 qw(encode_base64);
# $dbh ist ein bereits verbundener DBI-Handle
my $stmt = $dbh->prepare(q{UPDATE photos SET data = ? WHERE id = ?});
$stmt->execute(encode_base64($bytes, ""), 42);
Konfigurationsdateien haben dieselbe Form: ein JSON-Dokument, wo das binäre oder geheime Feld ein Einzeilen-Base64-String ist, genau deshalb existiert das zweite Argument:
use JSON::PP qw(encode_json);
my $config = {
api_key => encode_base64($key_bytes, ""),
logo_png => encode_base64($png_bytes, ""),
};
open my $fh, ">:raw", "app.json" or die $!;
print {$fh} encode_json($config);
close $fh;
Umgebungsvariablen verdienen ein Wort der Warnung. Base64 ist für kleine Tokens in der Umgebung gut, aber die kodierte Form ist 33 Prozent größer als das Original, und das Betriebssystem setzt eine Grenze für jedes Argument. Auf Linux liegt das Limit bei 128 KB pro String, durchgesetzt von execve, und ein großer Blob in einer Umgebungsvariablen schlägt nicht höflich fehl: Der Kindprozess stirbt mit einer kryptischen Fehlermeldung, in dem Moment, in dem er gespawnt wird. Kleine Werte in der Umgebung, große Werte in einer Datei oder einer Datenbank.
Performance: C, nicht Perl, erledigt die Arbeit
Das Core-Modul ist in C implementiert, und dieses C stammt ab von Code, der 1991 für metamail geschrieben wurde, was eine lustige Tatsache ist, bis Sie die Implikation bemerken: Der Encoder hat drei Jahrzehnte Tuning hinter sich. Auf einer modernen Maschine verarbeitet er Daten mit einem Tempo von Gigabytes pro Sekunde, schneller als die Festplatte oder das Netzwerk, die er normalerweise füttert, also ist Base64 selbst fast nie der Flaschenhals. Die I/O.
Für das ungewöhnliche System ohne C-Compiler liefert das reine Perl-Zwilling MIME::Base64::Perl auf CPAN dieselbe grundlegende Schnittstelle, ein paar Mal langsamer, aber für gewöhnliche Lasten immer noch bequem. Und zwei Gewohnheiten halten die großen Jobs berechenbar: Streamen Sie in 57-Byte-Chunks statt zu slurpen, und bestimmen Sie Ihre Puffer mit encoded_base64_length, bevor Sie zuweisen, was Ihnen sowohl das Ratespiel als auch die Neu-Zuweisung erspart.
Fallen, gerankt nach Nachmittagskosten
Die Fallen, in etwa der Reihenfolge, in der sie beißen:
| Falle | Was passiert | Lösung |
|---|---|---|
| Das zweite Argument zu vergessen | die Ausgabe kommt bei 76 Zeichen umgebrochen mit einem abschließenden Zeilenende, und Ihre URL, Ihr JSON-Feld oder Ihr Header bricht in der Mitte des Werts | geben Sie "" für die Einzeilen-Ausgabe, und behalten Sie den Umbruch für Ziele, die ihn erwarten |
| Umbruch in der falschen Breite | ein PEM-Konsument erwartet 64-Zeichen-Zeilen und bekommt 76, oder ein MIME-Body überschreitet die 76-Zeichen-Grenze | passen Sie die Breite an den Dialekt an: "" für keinen, "\r\n" für MIME, ein Helfer für PEM |
| Das Croak über das breite Zeichen | eine Zeichenkette mit Codes über 255 stirbt mitten im Request mit Breites Zeichen am Subroutine-Einstieg | führen Sie die Zeichen zuerst bewusst benannt durch Encode, vor dem encode_base64-Aufruf |
| Doppelte Kodierung | die Neu-Kodierung bereits UTF-8-Bytes durch encode("UTF-8", ...) macht aus Hëllo ein Hëllo |
Bytes werden genau einmal kodiert; prüfen Sie im Zweifel utf8::is_utf8() |
| Alphabete still zu mischen | ein Wert, kodiert mit - und _, trifft einen Standardalphabet-Decoder und kommt als Müll zurück |
ein Dialekt pro Wert, von Anfang bis Ende: Wählen Sie an der Grenze base64url oder Standard |
| Das abschließende Zeilenende | encode_base64 hängt das eol an, auch wenn die letzte Zeile exakt voll ist, und ein strenger Konsument sieht eine Leerzeile |
chomp oder rtrim das Ergebnis, wenn der Konsument pingelig ist |
| Umgebrochene Werte in einer Datenbank | Zeilenenden landen in einer TEXT-Spalte, und das nächste SELECT liefert einen kaputten Token zurück |
speichern Sie die Einzeilen-Form; umbrechen Sie nur am Ziel |
| Umgebungsvariablen mit großen Blobs | das 33-Prozent-Wachstum plus das OS-Limit pro Argument tötet den Kindprozess beim Spawnen mit einer kryptischen Fehlermeldung | kleine Werte in der Umgebung, große Werte in einer Datei oder einer Datenbank |
| Anzunehmen, Base64 sei Schutz | das Format versteckt nichts, und die öffentliche Dokumentation berichtet über echte Vorfälle, in denen ein Benutzer einen IMAP-Austausch einfügte und versehentlich ein Passwort verriet | behandeln Sie die Ausgabe ab dem Moment als vertraulich, in dem sie entsteht, und halten Sie sie aus Logs heraus |
| Planen ohne die Größenrechnung | ein 500-kB-Bild wird 670 kB Text, und das Speicher- oder Payload-Limit, das Sie nicht gecheckt haben, beißt | veranschlagen Sie 4/3 der Originalgröße, bevor Sie sich festlegen |
Eine Geschichte, erzählt vom Encoder
Perls Base64-Encoder hat eine Karriere, die eine Minute wert ist, und sie beginnt im ersten Web-Toolkit:
- Geboren in libwww perl. Der Encoder begann sein Leben als
LWP::Base64, geschrieben von Martijn Koster und Joerg Reichelt, das Gisle Aas in libwww perl alsMIME::Base64aufnahm; es promovierte im April 1997 zu seiner eigenen CPAN-Verteilung, Version 2.00, mit einem Changelog-Eintrag, der einfach nur sagt, es basiere auf libwww perl 5.08. - Die Speed-Ära. Version 2.07 aus 1998 lieferte eine schnellere und schlauere C-Implementierung des Decoders, etwa 25 Prozent zügiger auf den damals modernen Linux-Kisten, und das Tuning ging ein Jahrzehnt weiter.
- Die Unicode-Ära. Perl 5.8 aus 2002 brachte Zeichen mit Codes über 255 in gewöhnliche Strings, und das Modul antwortete in Etappen: 2.12 aus 2001 stufte UTF-8-Strings vor der Kodierung herab, und das moderne Breites Zeichen am Subroutine-Einstieg-Croak ist die Art des Encoders, dieses Versprechen zu halten. Die 2.13-Synchronisierung mit dem Core im selben Jahr brachte EBCDIC-Unterstützung mit, eine Erinnerung daran, dass Base64 in Perl immer noch auf Mainframes läuft.
- Die Command-Line-Ära. Veröffentlichungen von 2.14 aus 2003 bis 3.05 aus 2004 bündelten ein echtes
encode-base64-Kommando, zusammen mit seinen decode- und quoted-printable-Zwillingen; 3.06 aus 2005 verlegte die Skripte in eine eigene MIME Base64 Scripts-Verteilung. - Der URL-sichere Einzug. RFC 4648 standardisierte 2006 das URL-sichere Alphabet, ein eigenständiges
MIME::Base64::URLSafe-Modul erschien im selben Jahr, und das Core-Modul holte 2010 mit 3.11 auf, mitencode_base64urlin einem einzigen Aufruf. - Die moderne Linie. Version 3.16 aus 2020 baute das Packaging neu und hob die Untergrenze auf Perl 5.6; die aktuellen Core-Perls liefern die 3.16er-Serie, und das Modul wird innerhalb der Core-Verteilung gepflegt, was so sicher ein Zuhause ist, wie es ein Core-Modul nur haben kann.
Fun Facts, speziell Perl
Die Trivia, die diese Geschichte zu einer guten machen:
- Das POD-Beispiel ist eine magische Phrase. Seit 1997 kodiert die eigene Dokumentation des Moduls
Aladdin:open sesame, also ist der StringQWxhZGRpbjpvcGVuIHNlc2FtZQ==fast dreißig Jahre lang die Visitenkarte des Moduls. - Das Standard-Zeilenende ist das, das Sie wahrscheinlich nicht erwartet haben. Es ist ein gewöhnliches
\n, nicht das CRLF, das MIME spricht. Die eigene Konvention des RFCs braucht das zweite Argument, und das Modul kommt mit dem Standard des Programmierers, nicht dem des Protokolls. - Der leere String hat eine Sonderregel. Kodieren Sie nichts, und Sie bekommen nichts zurück, kein Zeilenende angehängt: die eine dokumentierte Ausnahme von der Regel über das abschließende eol und der Grund, warum eine leere Datei sauber hin und zurück wandert.
- Der IMAP-Verwandte hat ein Komma. Die Postfachnamen-Variante aus RFC 3501 tauscht das
/im Alphabet gegen ein Komma, also kann ein Base64-String von einem IMAP-Server einen Buchstaben enthalten, den der Standard-Decoder als Rauschen behandelt. - Die Abstammung von 1991 ist echt. Die C-Implementierung stammt ab von metamail, Bellcores Mail-Programm von 1991, drei Jahre, bevor Perl 5 geboren wurde, also ist jeder
encode_base64-Aufruf teilweise Neunziger-Code. - Das reine Perl-Zwilling hat seine eigene Geschichte. Als Version 3.00 aus 2004 die reinen Perl-Implementierungen aus dem Core-Modul strich, nannte das Changelog sie Ballast, der echte Probleme in den XS-Implementierungen versteckt, und veröffentlichte sie als
MIME::Base64::Perlneu, wo sie immer noch leben.
Wenn also das nächste Mal rohe Bytes durch eine rein textbasierte Welt reisen müssen, kennen Sie die ganze Geschichte. Ein Funktionsaufruf erledigt die Arbeit, das versteckte Zeilenende ist eine Entscheidung, die Sie mit dem zweiten Argument treffen, das Croak über das breite Zeichen ist die Art des Encoders, Ihr Unicode ehrlich zu halten, der URL-sichere Dialekt ist seit 2010 ein Aufruf, Dateien streamen in 57-Byte-Chunks, und die 33-Prozent-Rechnung ist der Preis des Eintritts. Und wenn Sie eines Tages die Reise in die andere Richtung machen müssen, einen String aus Buchstaben nehmen und die originalen Bytes zurückholen, deckt der verwandte Artikel über Base64-Dekodierung in Perl, verlinkt unten, dieses Ritual in derselben Tiefe ab.
Zuletzt aktualisiert: 2026-09-08
Verwandter Artikel: Base64-Dekodierung in Perl: Ein vollständiger Leitfaden