Haben Sie mit dem Base64-Format zu tun? Dann ist diese Website genau das Richtige für Sie! Nutzen Sie unser superpraktisches Online-Tool, um Ihre Daten zu kodieren oder zu dekodieren.

Base64-Kodierung in Perl: Ein vollständiger Leitfaden

Sie haben Daten, die einen Kanal überleben müssen, der sie nicht mag. Ein Binär-Blob, der in ein JSON-Feld muss. Ein Bild, das in einem HTML-Tag leben soll. Ein Zertifikat, das in eine Konfigurationsdatei gehört. Ein Token, das durch URLs, Header und Query-Strings reisen wird. Das ist das alltägliche Leben der Base64-Kodierung: Sie schreibt jeweils drei Bytes roher Daten als vier Zeichen aus einem 64-Buchstaben-Alphabet um, mit einem oder zwei =-Zeichen, die das Ende abschließen, sodass das Ergebnis schlichter Text ist, den alles tragen kann, typischerweise etwa 33 Prozent länger als der Ausgangspunkt. Die Startseite dieser Site erklärt das Format im Detail, also konzentriert sich dieser Artikel darauf, was Perl Ihnen gibt, was es still für Sie entscheidet und wo die Fallen stecken.

Die gute Nachricht zuerst: encode_base64 lebt seit 2002 im Core von Perl, es ist in C implementiert und es ist bequem schnell. Der interessante Teil ist, dass die Funktion Meinungen hat. Sie bricht ihre Ausgabe bei 76 Zeichen um, sie hängt ein abschließendes Zeilenende an, und sie kodiert keine Unicode-Zeichen, die Sie nicht zuerst in Bytes umgewandelt haben: Oberhalb des Latin-1-Bereichs stirbt es ohne Umschweife, und unterhalb dieser Linie geht es still von Latin-1-Bytes aus. Dieser Leitfaden geht jeden Geschmack von Base64 durch, den ein Perl-Entwickler tatsächlich produziert: den Einzeiler, den MIME-verpackten E-Mail-Body, den PEM-verpackten Schlüssel, das URL-sichere Token und die Streaming-Version für Dateien, die zu groß für den Speicher sind.

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 Zeilenende-Sequenz, und sie ist standardmäßig ein schlichtes Zeilenende, was heißt, dass der unschuldigste wirkende Aufruf in Perl umgebrochene, zeilenumbruchbeendete 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: leeren String für keinen Umbruch übergeben

Die Ausgabegröße folgt einem festen Muster, das Sie vor dem Aufrufen 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 einzelnen 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 unvollständige Gruppe, die mit einem oder zwei =-Zeichen aufgefüllt wird. Eine Konsequenz, die sich zu wissen lohnt: 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 tun, hat das Modul seit 3.10 aus dem Jahr 2010 eine Längen-Funktion. Sie ist 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 sie ist der einzige Weg, auf dem der Kodierer je schreit: Wenn der String, den Sie ihm übergeben, Zeichen mit einem Code über 255 enthält, stirbt encode_base64 mit Wide character in subroutine entry. Unterhalb dieser Linie ist das Versagen leiser: Zeichen bis 255 werden still auf ihre Latin-1-Bytes herabgestuft, also kodiert ein String mit Akzentzeichen, der die Umwandlung übersprungen hat, als Latin-1 statt UTF-8, und niemand sagt es Ihnen. Die Base64-Kodierung ist nur für Einzel-Byte-Zeichen definiert, und Perl 5.8 und besser erlauben erweiterte Zeichen in Strings, also ist die Umwandlung eine Entscheidung, die Sie mit Encode bewusst treffen, im nächsten Abschnitt direkt.

Text oder Bytes? Der Schritt, den die Funktion nicht tun kann

Perl-Strings tragen ein stilles Flag, das sagt, ob sie Zeichen oder Bytes enthalten, und Base64 lebt auf der Bytes-Seite dieser Linie. Wenn Ihr Text ein Zeichen-String ist, und es ist der Moment, in dem er aus einem JSON-Parser, einer Vorlage oder einem Literal mit Akzentbuchstaben in einer UTF-8-Quelldatei kommt, weigert sich der Kodierer zu raten, welche Bytes Sie für Zeichen oberhalb des Latin-1-Bereichs meinen, und es teilt Ihnen das mit; unterhalb des Bereichs ratet es Latin-1 in Stille. Die Reparatur 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";  # ein Zeichen-String
my $utf8  = encode("UTF-8", $chars);  # jetzt: Bytes
my $b64   = encode_base64($utf8, "");
print $b64, "\n";  # SMOrbGxvIFfDtnJsZA==

Dieser 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 geben Sie die Bytes an den Kodierer. Für veralteten westlichen Text, der als Windows-1252 ankam, ist die Umwandlung dieselbe Funktion mit einem anderen Namen, encode("Windows-1252", $legacy), die Ihnen die ursprüngliche Einzel-Byte-Form liefert. Das Encode-Modul ist Core, also kostet all das nichts.

Und jetzt die Falle. Wenn die Bytes, die Sie haben, bereits UTF-8 sind und Sie sie erneut durch encode("UTF-8", ...) jagen, in dem Glauben, Sie machten sie zu UTF-8, bekommen Sie keine Kopie: Sie bekommen eine doppelte Kodierung, in der jeder Akzentbuchstabe zu zwei eigenen Zeichen anschwillt. Das klassische Symptom ist Text, der früher Hëllo las und jetzt Hëllo liest, und jeder Dekodierer im Internet wird das treu 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 Faustregel, 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 Move; wenn es nicht gesetzt ist, halten Sie Bytes in den Händen, und Sie sind bereit für Base64.

Zeilen-Umbruch: Drei Dialekte, eine Regel

Weil die Standard-Ausgabe umgebrochen und zeilenumbruchbeendet ist, ist die erste Entscheidung bei jeder Kodier-Aufgabe ein Ziel-Problem: Wo wird dieser String leben? Der vereinigende Fakt ist, dass Dekodierer Zeilenumbrüche vollständig ignorieren: RFC 2045 sagt der Dekodier-Software, alle Zeilenumbrüche und Zeichen außerhalb des Alphabets zu ignorieren, also ist Umbruch eine Höflichkeit gegenüber zeilenbasierten Tools und Menschen, keine semantische Differenz. Die drei Antworten in der Praxis:

Keine Zeilenumbrüche. Die Funktions-Ausgabe mit deaktiviertem Umbruch, exakt eine Zeile. Das ist es, was Sie für URLs, JSON-Payloads, Header, Datenbank-Werte und alles andere wollen, wo ein Zeilenumbruch ein Bug wäre. Das ist auch das, was die meisten meinen, wenn sie nur nach dem 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. Diese ist der eigene Job des Moduls, erledigt 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 allein produzieren, also füllt ein Vier-Zeilen-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-Rüstung, deren Daten-Zeilen 64 Zeichen breit sind und deren abschließende CRC24-Prüfsummenzeile von GnuPG ergänzt wird und nicht von Ihnen. Ein Stolperstein gilt für alle: encode_base64 hängt das Zeilenende ans allerletzte Ende des Ergebnisses, auch wenn die letzte Zeile ihre Breite exakt füllt. Wenn ein nachgelagertes System über diese abschließende Leerzeile stolpert, behebt ein rtrim-Aufruf auf das Ergebnis das.

URL-sicherer Base64: Das - und _ Alphabet

Das Standardalphabet enthält + und /, und beide sind außerhalb einer Textdatei Ärger: Ein + in einem form-kodierten Query-String wird zu einem Leerzeichen, bevor Ihre Anwendung es je zu sehen bekommt, und / ist ein Pfadtrenner in URLs. Dateinamen und Tokens haben ihre eigenen Beschwerden. Abschnitt 5 von RFC 4648 löst das mit dem URL- und Dateinamen-sicheren Alphabet, in dem + zu - wird, / zu _ und das abschließende =-Padding in der Regel weggelassen wird. Der RFC besteht darauf, dass diese nicht als gleich mit der base64-Kodierung angesehen werden sollte, also behandeln Sie sie als eigenes Format, üblicherweise genannt base64url. Perl produziert es seit Version 3.11 aus dem Jahr 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 einzelne Aufruf macht 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 String-Operationen 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 Sie es verwenden: JSON-Web-Token-Teile, OAuth-State- und Nonce-Parameter, API-IDs, die Sie in URL-Pfade legen, und opake Schlüssel, die eine Adressleiste oder einen Dateinamen überleben müssen, wo CPANs Data::UUID::Base64URLSafe genau dafür existiert. Wann nicht: E-Mail-Bodies, PEM-Rüstung und jeder Ort, an dessen anderem Ende ein Standard-Alphabet-Verbraucher sitzt, weil - und _ nicht in ihrem Wortschatz stehen. 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 bietet das eigenständige MIME::Base64::URLSafe-Modul aus dem Jahr 2006, ein Port von Pythons urlsafe-Codec, urlsafe_b64encode; auf allem Modernen ist die eingebaute Funktion das richtige Werkzeug.

Ein JWT bauen: Jedes Teil von Hand

JSON Web Tokens sind der Vorzeigeverbraucher von Base64 in modernen APIs, und sie verwenden den URL-sicheren, ungepaddeten Dialekt aus dem Abschnitt oben. Laut RFC 7515 ist ein kompaktes JWT drei durch Punkte getrennte base64url-Teile: der geschützte Header, die Payload und die Signatur. Eines 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 kompaktes HS256-Token; die Schlüsselreihenfolge innerhalb jedes JSON-Teils variiert von Lauf zu Lauf

Drei Details, die sich zu bemerken lohnen. Erstens gibt encode_json aus dem Core-JSON::PP-Modul kompakte UTF-8-Bytes ohne Leerzeichen aus, was genau das ist, was die JOSE-Spezifikationen innerhalb eines Tokens wollen. Zweitens ist die Payload für jeden lesbar, und das ist Design: Ein JWT ist ein signierter Schein, kein Geheimnis, also legen Sie niemals vertrauliche Werte in die claims. Drittens ist die Signatur die base64url-Kodierung roher HMAC-Bytes, und deshalb geht hmac_sha256 ohne jegliche Hex-Formatierung direkt in den Kodierer.

Für die Produktion rollen Sie das Signieren 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 empfangenden Seite: pinnen Sie den Algorithmus mit accepted_alg fest, damit ein Angreifer das Token nicht auf eine schwächere Variante umschalten kann: decode_jwt(token => $jwt, key => $secret, accepted_alg => "HS256") verifiziert die Signatur und croakt bei einem Fehlschlag. Von Hand basteln ist gut fürs Verstehen; eine Bibliothek ist gut fürs Geld.

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. Das leere String-Zeichenargument ist hier tragend, denn ein abschließendes Zeilenende innerhalb eines Header-Felds 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: Die Payload sitzt direkt in der URL, also ist keine zweite Anfrage nötig, um sie zu holen. Binäre Medien verwenden das ;base64-Flag, also ist die Payload exakt das, was encode_base64 mit deaktiviertem Umbruch produziert:

my $uri = "data:" . $mime_type . ";base64," . encode_base64($bytes, "");

Die Trade-offs sind aber real. Die 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 versendet jeder Seitenaufruf die Bytes erneut als Teil des Dokuments, und der RFC selbst sagt, dass data URIs nur für kurze Werte nützlich sind. 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 machen 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, auf die Vier-Zeichen-Gruppe gepadded

Dateien: Slurps, 57-Byte-Chunks und die CLI

Die geradlinigste Kodier-Aufgabe: Eine Datei wird zu Text. Perl-Strings sind Bytes, also gibt es keinen Binärmodus, nach dem man suchen muss - die :raw-Lage ist alles. 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-Lagen sind wichtig. Ohne sie würde Perl versuchen, die Bytes beim Hereinkommen und Hinausgehen als Plattformtext zu interpretieren, und auf einem System mit einer anderen Standard-Kodierung ist das genau die Korruption, die Sie nicht sehen, bis die Datei woanders geöffnet wird. Und denken Sie an die Größen-Rechnung, wenn Sie Speicher planen: Ein 500-KB-Bild wird zu einer 670-KB-Textdatei, und ein 1-GB-Video wird zu 1,33 GB.

Für Dateien, die zu groß für den Speicher sind, gibt die Dokumentation des Moduls selbst die Regel: Kodieren Sie in Chunks, die ein Vielfaches von 57 Bytes sind, denn 57 Bytes Daten füllen exakt eine 76-Zeichen-Zeile, 76 ist 57 mal 4 geteilt durch 3. Chunks an dieser Grenze aufteilen, und Sie bekommen nie Padding mitten im Stream:

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 kurze, Chunk trägt das finale Padding, und das Ergebnis ist byte-für-byte dasselbe wie das Slurpen der ganzen Datei und das Kodieren auf einmal, nur mit konstanter Speicherspur. Und wenn Sie gar kein Skript brauchen, deckt der Einzeiler es ab, mit -0777, das die Eingabe slurpt, und dem leeren String-Argument, das die Ausgabe auf einer Zeile hält:

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, dass Daten, die nicht sicher als roher Text reisen können, mit Content-Transfer-Encoding: base64 gesendet werden sollten, in Zeilen von höchstens 76 Zeichen. Wenn Sie mit MIME::Lite Mail bauen, ist das Ganze ein einziges 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-einfachen Zeilenende des Moduls; der Mail-Transport ist es, der sie in CRLF verwandelt) und stempelt das Teil mit dem passenden Content-Transfer-Encoding-Header. Email::MIME nimmt die gleiche Haltung ein und base64-kodiert jeden Anhang, den Sie ihm geben, als rohen Daten-String (seine Doku: "alle auf diese Weise erstellten Teile werden mit base64 kodiert, einfach auf der sicheren Seite"). Wenn Sie eine rohe MIME-Nachricht von Hand zusammenstellen, ist das Äquivalent die zwei Zeilen aus dem Umbruch-Abschnitt, encode_base64($bytes, "\r\n") plus die Header-Zeile, und das ist die ganze Protokoll-Seite der Geschichte.

Datenbanken, Konfiguration und Umgebungsvariablen

Datenbanken: Binärdaten reisen oft als Base64 in einer TEXT-Spalte, weil die Spalte nicht zusichern kann, dass sie beliebige Bytes unverändert durchlässt. Speichern Sie die Ein-Zeilen-Form, niemals die umgebrochene, sonst gibt Ihr nächstes SELECT eine Zeichenkette mit Zeilenumbrücken mitten im Wert 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 sind dasselbe Muster: ein JSON-Dokument, in dem das binäre oder geheime Feld ein Ein-Zeilen-Base64-String ist, was genau der Grund ist, warum das zweite Argument existiert:

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 in Ordnung, aber die kodierte Form ist 33 Prozent größer als das Original, und das Betriebssystem begrenzt jedes Argument. Auf Linux liegt die Grenze bei 128 KB pro String, erzwungen von execve, und ein großer Blob in einer Umgebungsvariablen schlägt nicht artig fehl: Der Kind-Prozess stirbt in dem Moment, in dem er gespawnt wird, mit einem kryptischen Fehler. Kleine Werte in die Umgebung, große Werte in eine Datei oder eine Datenbank.

Performance: C, nicht Perl, macht die Arbeit

Das Core-Modul ist in C implementiert, und dieses C stammt von Code, der 1991 für metamail geschrieben wurde, was eine lustige Tatsache ist, bis man die Implikation bemerkt: Der Kodierer hat drei Jahrzehnte Tuning hinter sich. Auf einer modernen Maschine verarbeitet er Daten in einem Tempo von Gigabytes pro Sekunde, das ist schneller als die Festplatte oder das Netzwerk, die er normalerweise füttert, also ist Base64 selbst fast nie der Engpass. Das I/O schon.

Für das seltsame System ohne C-Compiler bietet der reine Perl-Zwilling MIME::Base64::Perl auf CPAN dasselbe grundlegende Interface, ein paar Mal langsamer, aber für gewöhnliche Arbeitslasten immer noch bequem. Und zwei Gewohnheiten halten die großen Jobs vorhersehbar: Streamen Sie mit 57-Byte-Chunks statt zu slurpen, und legen Sie Ihre Puffer mit encoded_base64_length vor der Allokation fest, was Ihnen sowohl das Raten als auch die Neu-Allokation erspart.

Fallen, gerankt nach Nachmittagskosten

Die Fallen, in grob der Reihenfolge, in der sie beißen:

Falle Was passiert Reparatur
Das zweite Argument vergessen die Ausgabe kommt bei 76 Zeichen umgebrochen mit einem abschließenden Zeilenende an, und Ihre URL, Ihr JSON-Feld oder Ihr Header bricht mitten im Wert "" für Ein-Zeilen-Ausgabe übergeben, und den Umbruch für Ziele behalten, die ihn erwarten
Mit der falschen Breite umbrechen ein PEM-Verbraucher erwartet 64-Zeichen-Zeilen und bekommt 76, oder ein MIME-Body überschreitet die 76-Zeichen-Grenze die Breite an den Dialekt anpassen: "" für keinen, "\r\n" für MIME, ein Helfer für PEM
Der Wide-Character-Croak ein Zeichen-String mit Codes über 255 stirbt mitten im Request mit Wide character in subroutine entry die Zeichen zuerst durch Encode jagen, bewusst benannt, vor dem encode_base64-Aufruf
Doppelte Kodierung bereits UTF-8 Bytes erneut durch encode("UTF-8", ...) zu kodieren verwandelt Hëllo in Hëllo Bytes werden genau einmal kodiert; im Zweifel utf8::is_utf8() prüfen
Alphabete still mischen ein mit - und _ kodierter Wert trifft auf einen Standard-Alphabet-Dekodierer und kommt als Müll zurück ein Dialekt pro Wert, von Anfang bis Ende: am Rand base64url oder Standard wählen
Das abschließende Zeilenende encode_base64 hängt das eol an, selbst wenn die letzte Zeile exakt voll ist, und ein strikter Verbraucher sieht eine Leerzeile chomp oder rtrim auf dem Ergebnis, wenn der Verbraucher pingelig ist
Umbrochene Werte in einer Datenbank Zeilenumbrüche landen in einer TEXT-Spalte und das nächste SELECT gibt ein kaputtes Token zurück die Ein-Zeilen-Form speichern; nur am Ziel umbrechen
Umgebungsvariablen mit großen Blobs das 33-Prozent-Wachstum plus die OS-Argumentgrenze tötet den Kind-Prozess beim Spawn mit einem kryptischen Fehler kleine Werte in die Umgebung, große Werte in eine Datei oder eine Datenbank
Annehmen, Base64 sei Schutz das Format versteckt nichts, und die öffentliche Dokumentation listet echte Vorfälle, in denen ein Benutzer einen IMAP-Verkehr einklebte und versehentlich ein Passwort preisgab die Ausgabe ab dem Moment, in dem sie produziert wird, als vertraulich behandeln, und aus Logs heraushalten
Planen ohne die Größen-Rechnung ein 500-KB-Bild wird zu 670 KB Text, und die Speicher- oder Payload-Grenze, die Sie nicht gecheckt haben, beißt 4/3 der Originalgröße einplanen, bevor Sie sich festlegen

Eine Geschichte, erzählt vom Kodierer

Perls Base64-Kodierer hat eine Karriere, die eine Minute wert ist, und sie beginnt im ersten Web-Toolkit:

  • Geboren in libwww perl. Der Kodierer begann sein Leben als LWP::Base64, geschrieben von Martijn Koster und Joerg Reichelt, den Gisle Aas als MIME::Base64 in libwww perl aufnahm; er machte im April 1997 den Sprung zu einer eigenen CPAN-Distribution, Version 2.00, mit einem Changelog-Eintrag, der schlicht sagt, dass er auf libwww perl 5.08 basiert.
  • Die Geschwindigkeits-Ära. Version 2.07 aus dem Jahr 1998 lieferte eine schnellere und klügere C-Implementierung des Dekodierers, auf den damaligen modernen Linux-Maschinen etwa 25 Prozent schneller, und das Tuning ging ein Jahrzehnt weiter.
  • Die Unicode-Ära. Perl 5.8 aus dem Jahr 2002 brachte Zeichen mit Codes über 255 in gewöhnliche Strings, und das Modul antwortete in Schritten: 2.12 aus dem Jahr 2001 stufte UTF-8-Strings vor der Kodierung herab, und der moderne Wide character in subroutine entry-Croak ist der Weg des Kodierers, das Versprechen zu halten. Die 2.13-Synchronisation mit dem Core in demselben Jahr brachte EBCDIC-Unterstützung mit, eine Erinnerung daran, dass Base64 in Perl immer noch auf Mainframes läuft.
  • Die Kommandozeilen-Ära. Releases von 2.14 aus dem Jahr 2003 bis 3.05 aus dem Jahr 2004 bündelten einen echten encode-base64-Befehl, zusammen mit seinen decode- und quoted-printable-Zwillingen; 3.06 aus dem Jahr 2005 verlegte die Skripte in eine eigenständige MIME Base64 Scripts-Distribution.
  • Die Ankunft des URL-sicheren. RFC 4648 standardisierte das URL-sichere Alphabet 2006, ein eigenständiges MIME::Base64::URLSafe-Modul erschien im selben Jahr, und das Core-Modul schloss 3.11 aus dem Jahr 2010 mit encode_base64url in einem einzigen Aufruf auf.
  • Die moderne Linie. Version 3.16 aus dem Jahr 2020 baute das Packaging neu auf und hob die Untergrenze auf Perl 5.6 an; die aktuellen Core-Perls liefern die 3.16-Serie, und das Modul wird innerhalb der Core-Distribution gewartet, was so sicher ein Zuhause ist, wie es ein Core-Modul nur haben kann.

Fun-Fakten, speziell Perl

Die Trivia, die diese Geschichte zu einer guten machen:

  • Das POD-Beispiel ist ein magischer Satz. Seit 1997 kodiert die Dokumentation des Moduls selbst Aladdin:open sesame, und deshalb ist der String QWxhZGRpbjpvcGVuIHNlc2FtZQ== seit fast dreißig Jahren die Visitenkarte des Moduls.
  • Das Standard-Zeilenende ist das, das Sie vermutlich nicht erwartet haben. Es ist ein schlichtes \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 des abschließenden eol, und der Grund, warum eine leere Datei sauber hin und zurück wandert.
  • Der IMAP-Cousin hat ein Komma. Die Postfach-Namen-Variante aus RFC 3501 tauscht das / im Alphabet gegen ein Komma, und deshalb kann ein Base64-String von einem IMAP-Server einen Buchstaben enthalten, den der Standard-Dekodierer als Rauschen behandelt.
  • Die Abstammung von 1991 ist real. Die C-Implementierung stammt von metamail, Bellcores Mailprogramm von 1991, drei Jahre vor der Geburt von Perl 5, und deshalb ist jeder encode_base64-Aufruf teilweise Neunziger-Code.
  • Der reine Perl-Zwilling hat eine eigene Geschichte. Als Version 3.00 aus dem Jahr 2004 die reinen Perl-Implementierungen aus dem Core-Modul warf, nannte der Changelog sie Ballast, der echte Probleme in den XS-Implementierungen versteckt, und veröffentlichte sie als MIME::Base64::Perl neu, wo sie immer noch leben.

Wenn also das nächste Mal rohe Bytes durch eine nur-Text-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, der Wide-Character-Croak ist der Weg des Kodierers, Ihren 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 Eintrittspreis. Und wenn Sie eines Tages die Reise in die andere Richtung machen müssen, eine Zeichenkette aus Buchstaben nehmen und die originalen Bytes wieder herausbekommen, 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