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 R: Ein vollständiger Leitfaden

Sie haben Bytes, die reisen müssen, und die Straße erlaubt nur Text. Ein JPEG, das in einem JSON-Feld leben muss. Ein Zertifikat, das in einer Umgebungsvariable sitzen muss. Ein Plot, der in einem in sich geschlossenen HTML-Report reisen muss. Base64 ist das Packband für all das: Jede Abfolge von Bytes wird zu einem String aus 64 harmlosen Zeichen, der jeden Textkanal überlebt, den Sie ihm zumuten. Die Startseite dieser Site behandelt das Format in vollem Umfang, also hier die Kurzfassung: Drei Bytes gehen hinein, vier Zeichen kommen heraus, gewählt aus A bis Z, a bis z, 0 bis 9, dazu + und /, und ein wenig =-Padding am Ende, wenn die Fracht sich nicht gleichmäßig teilen lässt.

Der R-spezifische Twist: Base R liefert überhaupt keinen Base64-Kodierer mit. Es wartet kein base64_encode() in einem Basis-Paket, und es gibt keinen Einzeiler, der als eingebaute Funktion zur Hand wäre. Sie wählen ein Paket, und das Ökosystem gibt Ihnen eine echte Auswahl, mit unterschiedlichen Geschwindigkeiten, unterschiedlichen Umwickel-Gewohnheiten und unterschiedlichen Meinungen über Padding. Am Ende dieses Artikels wissen Sie, zu welchem Kodierer Sie in jeder Situation greifen, und welches von ihnen stillschweigend etwas anderes tut als zu kodieren.

Die Kodier-Landschaft

Fünf Pakete erledigen das Kodieren, und sie teilen sich in die Alltags-Arbeitstiere, die Krypto-Nachbarn und die kleinen Spezialisten. Das ist die Besetzung, Stand 2026:

Paket Version (2026) Kodier-Einstiegspunkte Umwickel-Gewohnheiten Greifen Sie zu, wenn
base64enc 0.1-6 base64encode() linewidth und newline, ganz von Ihnen zu bestimmen Alltags-Strings, MIME-Umwicklung
openssl 2.4.2 base64_encode() Zeilen à 64 Zeichen, LF-Brüche, Zeilenumbruch am Ende PEM-Dateien, bestehende Krypto-Stacks
b64 0.1.7 encode(), encode_file() umbricht nie; b64_chunk() und b64_wrap() auf Abruf Geschwindigkeit, Vektoren, URL-sichere Engines
base64 2.0.2 encode() standardmäßig Zeilen à 64 Zeichen plus Zeilenumbruch am Ende Datei-zu-Datei-Kleinarbeit, Report-Bilder
base64url 1.4 base64_urlencode() umbricht nie, kein Padding, Zeichen hinein URL-sichere Strings

Drei weitere Kodierer verstecken sich in Paketen, die Sie womöglich schon laden. jsonlite exportiert base64_enc() und base64url_enc(), und wenn Sie bereits JSON parsen, haben Sie womöglich schon einen Kodierer in der Hand. jose exportiert base64url_encode() für JWT-Arbeit. Und das Veteranen-Paket RCurl trägt immer noch einen base64()-Wrapper um libcurl, der bestens funktioniert und in eine frühere Ära gehört. Das base64-Paket schließlich beschreibt sich jetzt auf der Dose selbst als Kompatibilitäts-Wrapper und verweist neue Anwendungen auf base64enc, openssl oder jsonlite.

Einrichten

Wenn R noch nicht auf dem Rechner ist, liefert Ihr Betriebssystem es mit: r-base auf Debian und Ubuntu, R auf Fedora, Homebrew oder der offizielle Installer auf macOS, ein Installer auf Windows. Dann die Kodierer, direkt von CRAN:

install.packages("base64enc")
install.packages("openssl")
install.packages("b64")
install.packages("base64url")

Die beiden Orte, an denen Installationen schiefgehen, liegen beide im Build. openssl kompiliert gegen Ihr System-OpenSSL, und eine nackte Linux-Kiste will zuerst die Entwicklungs-Header (sudo apt install libssl-dev), oder Sie überspringen auf Debian und Ubuntu die Kompilierung komplett mit sudo apt install r-cran-openssl. Und b64 ist eine Rust-Engine, die mit extendr verpackt ist, so dass ein Quellcode-Build die Rust-Toolchain verlangt (sudo apt install cargo zieht auch rustc mit). Windows und macOS bekommen vorgebaute Binaries von CRAN, und nichts davon gilt.

Das erste Encoding

Neunzig Prozent des Kodier-Alltags passen in drei Zeilen, mit demselben berühmten String, den die Decode-Seite als Smoke-Test benutzt:

library(base64enc)
packed <- base64encode(charToRaw("Man"))
packed
#> [1] "TWFu"
identical(packed, "TWFu")
#> [1] TRUE

Drei Dinge fallen in dieser Zeremonie auf. Erstens ist die Eingabe ein raw vector: charToRaw() ist die Brücke von Ihrem R-String zu den Bytes, die gepackt werden, und die Alltags-Kodierer in dieser Tabelle - base64enc, openssl, b64 - nehmen alle raw entgegen. Zweitens ist die Ausgabe die entgegengesetzte Richtung zu den Dekodierern: ein einzelner Character-String, denn Kodieren endet auf der Text-Seite der Grenze. Drittens sehen Sie die Längen-Rechnung in Aktion: drei Bytes hinein, vier Zeichen heraus, kein Padding nötig, weil die Fracht sich gleichmäßig teilen lässt. Teilt sie sich nicht, landen ein oder zwei =-Zeichen am Ende.

Und weil ein Kodierer, dem Sie nicht trauen, schlimmer ist als keiner, hier ist der Round Trip, der beweist, dass sich beide Richtungen einig sind:

text <- "Hello, world!"
packed <- base64encode(charToRaw(text))
packed
#> [1] "SGVsbG8sIHdvcmxkIQ=="
identical(text, rawToChar(base64decode(packed)))
#> [1] TRUE

Strings, Bytes und die Dateiname-Falle

Jetzt die Falle, denn jeder R-Entwickler fällt einmal hinein. base64encode() behandelt ein character-Argument als Dateinamen, nicht als Text, der kodiert werden soll. Geben Sie ihm einen String, und er macht sich auf die Suche nach genau dieser Datei:

base64encode("Man")
#> Warning in file(what, "rb") :
#>   cannot open file 'Man': No such file or directory
#> Error: cannot open the connection
base64encode(charToRaw("Man"))
#> [1] "TWFu"

Die Warnung ist der Hinweis: Er versuchte file("Man", "rb"), was so viel heißt wie "eine Datei namens Man zum rohen Lesen öffnen". Bei base64encode() ist die Disziplin also ein einziger Reflex: zuerst charToRaw(), immer. Wenn tatsächlich eine Datei gemeint ist, tut die Funktion genau das, und die Ausgabe sind die gepackten Bytes der Datei, was manchmal genau das ist, was Sie wollen.

b64 nimmt zu derselben Frage eine andere Position ein: sein encode() akzeptiert einen Character-Vektor direkt, behandelt jedes Element als UTF-8-Text und ist obendrein vektorisiert:

b64::encode("Man")
#> [1] "TWFu"
b64::encode(c("Man", "M"))
#> [1] "TWFu" "TQ=="

Zwei weitere Kanten dieser Grenze lohnen es, sie zu kennen. Die leere Eingabe wird auf drei verschiedene Arten kodiert, je nachdem, wen man fragt:

base64encode(raw(0))
#> character(0)
b64::encode("")
#> [1] ""
base64url::base64_urlencode("")
#> [1] ""

base64enc antwortet mit einem Character-Vektor der Länge null, nicht mit einem leeren String, so dass Folge-Code, der einen String erwartet und character(0) bekommt, an überraschenden Orten scheitert. Und die Zeichensatz-Entscheidung lebt auch auf der Kodier-Seite: charToRaw() packt den String in die Kodierung, die er gerade trägt, so dass UTF-8-Text als UTF-8-Bytes reist, was das andere Ende der Leitung erwartet. Emoji inklusive, denn modernes R speichert Codepunkte oberhalb von U+FFFF als echtes UTF-8:

emoji <- "\U0001F600"
nchar(emoji, type = "bytes")
#> [1] 4
round_trip <- base64decode(base64encode(charToRaw(emoji)))
identical(emoji, rawToChar(round_trip))
#> [1] TRUE

Zeilenumbrüche: MIME, PEM und Ihre eigene Breite

Lange Base64-Strings werden in Zeilen umgebrochen, denn die ältesten Textkanäle der Welt hatten Spalten-Limits, und MIME kam nie dazu, sie zu vergessen. Die zwei historischen Umbrüche, die Sie treffen werden, sind MIME, der bei 76 Zeichen umbricht und zwischen den Zeilen CRLF setzt, und PEM, der bei 64 umbricht. Jeder Kodierer hat seine eigene Meinung, welchen davon er nutzen soll, falls überhaupt einen, und so ist das der Abschnitt, in dem Sie den Vertrag wählen, bevor Sie kodieren.

Zuerst die Größen-Rechnung, denn Umbrüche drehen sich darum zu wissen, wie lang die Ausgabe wird. Für jede drei Bytes kommen vier Zeichen heraus, was die Länge der kodierten Form zu einer simplen Obergrenze macht:

nchar(base64encode(charToRaw(strrep("a", 100))))
#> [1] 136
4 * ceiling(100 / 3)
#> [1] 136

Mit dem Wissen in der Tasche, die Kodierer. base64enc ist der flexibelste: Standardmäßig gibt es eine ununterbrochene Zeile aus, und das linewidth-Argument übergibt Ihnen stattdessen einen Vektor aus Zeilen:

long <- charToRaw(strrep("R", 100))
base64encode(long, linewidth = 76)
#> [1] "UlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJS"
#> [2] "UlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUg=="
base64encode(long, linewidth = 76, newline = "\r\n")
#> [1] "UlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJS\r\nUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUg=="

Zwei Zeilen für 100 Bytes bei Breite 76, standardmäßig ein Vektor, ein einzelner CRLF-verbundener String, wenn Sie newline hinzufügen. Es gibt kein leeres Element am Ende: 114 Bytes, die exakt zwei Zeilen à 76 ergeben, kommen als zwei Zeilen zurück, nicht als drei.

openssl nimmt den anderen Pol ein. linebreaks = TRUE umbricht bei 64 Zeichen mit einfachen LF-Brüchen und fügt ganz am Ende noch einen Zeilenumbruch hinzu:

wrapped <- openssl::base64_encode(charToRaw(strrep("a", 100)), linebreaks = TRUE)
nchar(wrapped)
#> [1] 139          # 136 Datenzeichen plus 3 Zeilenumbrüche
nchar(strsplit(wrapped, "\n", fixed = TRUE)[[1]])
#> [1] 64 64 8

Rechnen Sie nach: 136 Datenzeichen, zwei innere Brüche, ein nachfolgender Bruch, 139 gesamt. Und dieser Bruch am Ende ist für die natürlichste Zeilenzählung, die Sie schreiben können, unsichtbar, denn strsplit() wirft ein leeres Stück am Ende weg, so dass der Vektor drei Zeilen sagt, während der String vier trägt. Wenn Sie jemals eine OpenSSL-umwickelte Ausgabe mit einer MIME-umwickelten diffen und die Zeichenzahlen nicht aufgehen, ist das der Geist.

b64 umbricht überhaupt nicht; es gibt Ihnen die beiden Operationen separat, und die Blockbreite hat eine Regel, ein Vielfaches von vier, denn die Engine verweigert es, eine Base64-Gruppe in der Mitte zu durchschneiden:

enc <- b64::encode(strrep("a", 100))
ch <- b64::b64_chunk(enc, 76)
b64::b64_wrap(ch, "\r\n")
#> [1] "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\r\nYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=="
b64::b64_chunk(enc, 75)
#> Error: Chunk size must be a multiple of 4.

Und das dateiorientierte base64-Paket folgt OpenSSLs Vorbild: Zeilen à 64 Zeichen, ein Zeilenumbruch am Ende, standardmäßig an:

writeBin(charToRaw(strrep("b", 200)), "big.bin")
base64::encode("big.bin", "big.b64")
con <- file("big.b64")
lines <- readLines(con)
close(con)
nchar(lines)
#> [1] 64 64 64 64 12  0

Diese letzte Null ist der Zeilenumbruch am Ende, den readLines() als leere letzte Zeile einfängt. Hier ist das gesamte Feld auf einen Blick:

Kodierer Breite Zeilenumbruch Zeilenumbruch am Ende
base64encode(x) eine Zeile kein kein
base64encode(x, linewidth = 76, newline = "\r\n") 76 CRLF kein
openssl::base64_encode(x, linebreaks = TRUE) 64 LF ja
b64::encode(x) eine Zeile kein (mit b64_chunk() und b64_wrap() umwickeln) kein
base64::encode(in, out) 64 LF ja
base64url::base64_urlencode(x) eine Zeile kein kein

URL-sicheres Base64

Standard-Base64 gibt die letzten zwei Plätze seines Alphabets an + und /, und genau das sind die Zeichen, die URLs nicht lieben: das Plus wird zu %2B, der Schrägstrich zu %2F, und jedes = des Paddings wird zu %3D. Die URL-sichere Variante, definiert in RFC 4648 Abschnitt 5, tauscht diese beiden Buchstaben gegen - und _ aus und lässt meist das Padding weg, so dass ein Token, das überall einfügbar sein soll, überall einfügbar bleibt. R hat drei Türen hinein.

Die b64-Engines sind die vollständigsten: Eine Engine ist ein konfiguriertes Alphabet und eine Padding-Politik, und das Paket liefert die vier, die Sie brauchen. Füttern Sie dieselben drei Bytes in die Standard-Engine und in die URL-sichere, und sehen Sie dem Alphabet bei der Arbeit zu:

bytes <- as.raw(c(0xfb, 0xef, 0xbe))
b64::encode(bytes)
#> [1] "++++"
b64::encode(bytes, engine("url_safe"))
#> [1] "----"
b64::encode(as.raw(0x4d), engine("url_safe"))
#> [1] "TQ=="
b64::encode(as.raw(0x4d), engine("url_safe_no_pad"))
#> [1] "TQ"

Vier Engines, "standard", "standard_no_pad", "url_safe" und "url_safe_no_pad", und dasselbe Engine-Objekt funktioniert in beide Richtungen, was Ihren Code symmetrisch hält. Die no-padding-Varianten unterscheiden sich nur, wenn Padding tatsächlich aufkommen würde, wie im Ein-Byte-Beispiel oben.

Das dedizierte base64url-Paket ist die zweckgebundene Tür: Zeichen hinein, URL-sicherer String heraus, umbricht nie, paddet nie:

base64url::base64_urlencode("hello world")
#> [1] "aGVsbG8gd29ybGQ"

Und jose exportiert sein eigenes base64url_encode() für JWT-Arbeit und gibt auf der Decode-Seite raw vectors zurück, wie man sich das wünscht. Eine Regel bindet alle drei Türen: Das Alphabet, mit dem Sie kodieren, ist das Alphabet, mit dem Sie dekodieren müssen. Geben Sie einem Standard-Dekodierer den String "----", und er scheitert am ersten Strich; der Schwesteraartikel über das Dekodieren behandelt, wie jeder Dekodierer auf schmutzige oder nicht passende Eingabe trifft.

JWTs: Den Token bauen

Der JSON Web Token ist der Ort, an dem URL-sicheres Base64 zum Alltagsfahrer wurde. Ein JWT besteht aus drei base64url-Teilen, verbunden durch Punkte: ein Header, der beschreibt, wie er signiert wurde, ein Payload aus JSON-Claims und eine Signatur, die die beiden bindet. Auf der Kodier-Seite bauen Sie alle drei, und das jose-Paket erledigt die ganze Zeremonie. Seine API ist um die jwt_*-Funktionen herum gebaut (jwt_claim(), jwt_encode_hmac(), jwt_decode_hmac(), jwt_split()); das 2.0-Release (April 2026) fügt ED25519-Unterstützung hinzu und macht das typ-Headerfeld optional:

library(jose)
claim <- jwt_claim(iss = "app", sub = "1234567890", exp = Sys.time() + 3600)
token <- jwt_encode_hmac(claim, "0123456789abcdef")
sp <- jwt_split(token)
sp$header
#> $typ
#> [1] "JWT"
#>
#> $alg
#> [1] "HS256"
names(sp$payload)
#> [1] "iss" "sub" "exp" "iat"

Beachten Sie, was jwt_claim() hinter den Kulissen getan hat: iat (issued at) hat die aktuelle Zeit als Vorgabe, so dass es ohne Aufforderung im Payload auftauchte, während exp auf nichts setzt, was einen Token ohne Ablaufdatum bedeutet, was meist nicht das ist, was Sie wollen. Setzen Sie exp bewusst, und die Signatur kümmert sich um den Rest. jwt_split() ist das Inspektions-Werkzeug: der Header, der Payload als benannte Liste und die rohe Signatur, ohne Verifikation, genau der Hinschritt, den die Decode-Seite dieses Artikel-Paares beschreibt.

Die Verifikations-Seite ist der Ort, an dem jose seinen Lohn verdient. jwt_decode_hmac() prüft die Signatur und erzwingt die Zeit-Claims, und sein Verweigerungs-Stil ist spezifisch:

round <- jwt_decode_hmac(token, "0123456789abcdef")
round$sub
#> [1] "1234567890"
jwt_decode_hmac(token, "ffffffffffffffff")
#> Error: HMAC signature verification failed!
old <- jwt_claim(sub = "1234567890", exp = Sys.time() - 3600)
expired <- jwt_encode_hmac(old, "0123456789abcdef")
jwt_decode_hmac(expired, "0123456789abcdef")
#> Error: Token has expired on 2026-08-30 05:09:36

Bei Erfolg bekommen Sie die Claims als gewöhnliche Liste zurück, so dass round$sub und Freunde einfach funktionieren. Ein nbf-Claim (not before) in der Zukunft erntet seine eigene Verweigerung, Token is not valid before ..., und ein HMAC-Token wird nicht vom asymmetrischen jwt_decode_sig() dekodiert, das mit Unsupported algorithm: HMAC antwortet und stattdessen einen öffentlichen Schlüssel will. Zwei letzte Hinweise. Erstens ist der Payload kodiert, nicht verschlüsselt: Jeder kann jeden Claim lesen, also gehört nichts Geheimnisvolles hinein. Zweitens: Wenn Sie httr2 nach jose laden, beachten Sie die Masking-Meldung: httr2 exportiert seine eigenen jwt_claim(), jwt_encode_hmac() und jwt_encode_sig(), gebaut für OAuth-Client-Credentials mit exp fünf Minuten in der Zukunft als Vorgabe, und sie überschatten die jose-Versionen für den Rest der Session.

Dateien: Binär rein, Text raus

Eine Datei zu kodieren ist der Spiegel der Datei-Arbeit auf der Decode-Seite, und das b64-Paket hat den klarsten Einstiegspunkt, der auch der schnellste ist, denn es baut nie einen riesigen Zwischen-String:

writeBin(charToRaw("file payload bytes"), "payload.bin")
enc <- b64::encode_file("payload.bin")
enc
#> [1] "ZmlsZSBwYXlsb2FkIGJ5dGVz"
cat(enc, file = "payload.b64")
readLines("payload.b64")
#> Warning in readLines("payload.b64") :
#>   incomplete final line found on 'payload.b64'
#> [1] "ZmlsZSBwYXlsb2FkIGJ5dGVz"

Die Warnung ist eine getarnte Funktion: cat() fügt keinen Zeilenumbruch am Ende hinzu, also endet die Datei mitten in der Zeile, und readLines() teilt es Ihnen mit. Merken Sie sich, auf welcher Seite dieses Faktums Sie stehen, wenn die Datei von b64::decode_file() konsumiert wird, das bei einem Zeilenumbruch am Ende panikt; der Decode-Artikel hat die ganze Geschichte. Schreiben Sie mit cat() oder writeBin(charToRaw(enc), path), und die Kante bleibt stumpf.

Das base64-Paket ist die reine Datei-zu-Datei-Option, mit dem passenden Paar von Funktionen und OpenSSL-artiger Umwicklung als Vorgabe:

base64::encode("payload.bin", "payload2.b64")
readLines("payload2.b64")
#> [1] "ZmlsZSBwYXlsb2FkIGJ5dGVz" ""

Es gibt den Ausgabe-Pfad zurück, was für Logs praktisch ist. Und wenn Sie den Mechanismus sehen wollen, funktioniert die manuelle Pipeline mit jedem Kodierer in der Tabelle: Datei als raw lesen, kodieren, den Text schreiben, fertig:

bytes <- readBin("payload.bin", what = "raw", n = file.size("payload.bin"))
enc <- base64encode(bytes)
writeLines(enc, "payload3.b64")
identical(enc, readLines("payload3.b64"))
#> [1] TRUE

Data-URIs und in sich geschlossene Dokumente

Das data:-URI-Schema (RFC 2397) ist der sichtbarste Kunde der Kodier-Seite: ein Dokument, das seinen eigenen Inhalt mit sich trägt, einen MIME-Typ, das Wort "base64" und den Payload, alles in einem einzigen Attribut. Die PNG-Zauber-Bytes machen das Muster wiedererkennbar: Jedes Base64-PNG in freier Wildbahn beginnt mit denselben Zeichen, denn der Dateikopf 89 50 4E 47 kodiert immer auf dieselbe Weise:

png_head <- as.raw(c(0x89, 0x50, 0x4e, 0x47))
uri <- paste0("data:image/png;base64,", base64encode(png_head))
uri
#> [1] "data:image/png;base64,iVBORw=="
tag <- paste0("<img src=\"", uri, "\" />")

Wenn Sie jemals in einer HTML-Datei nach iVBOR ggreppt haben, um eingebettete Bilder zu finden, wissen Sie, warum der Fingerabdruck funktioniert. R-Markdown-Reports, Single-File-Dashboards und gescrapte Seiten nutzen alle dieselbe Form, und das Bauen eines folgt demselben Muster: Datei als raw lesen, kodieren und hinter dem MIME-Präfix einkleben. Der Preis ist die Größen-Rechnung von früher, ein Drittel größer als das Original, das für immer in Ihrem HTML sitzt, also halten Sie die eingebetteten Bilder schlank.

APIs und Web-Anfragen

Die Decode-Seite dieses Artikel-Paares trifft APIs, die Ihnen Base64 reichen; diese Seite trifft APIs, die es haben wollen. Das Muster ist jedes Mal dasselbe: die Bytes kodieren, den String in den JSON-Körper legen, abschicken. Der moderne HTTP-Client ist httr2:

library(httr2)
req <- request("https://httpbin.org/post")
req <- req_body_json(req, list(file = packed, name = "man.txt"))
req
#> <httr2_request>
#> POST https://httpbin.org/post
#> Body: JSON data
res <- req_perform(req)
res$status_code
#> [1] 200

Drei Hinweise für den Weg. Der Request-Konstruktor ist request(), und er trägt diesen Namen seit dem ersten Release von httr2. Der Antwort-Körper kommt, wenn Sie Antworten lesen, als raw vector an, also rawToChar() vor dem Parsen. Und die API kann einen Dialekt sprechen: Manche wollen das URL-sichere Alphabet, manche das Padding entfernt, und eine JSON-API hat kein Problem mit = in einem String-Wert, so dass die Padding-Frage nur dann zur URL-Frage wird, wenn das Base64 im Pfad oder in der Query reist. Im Zweifel lesen Sie die Beispiele der API, statt sich an die Spezifikation in Ihrem Kopf zu halten.

Datenbanken, Konfiguration und Umgebung

Base64 in einer Datenbank ist binäre Fracht, die durch eine Text-Spalte schmuggelt, und die Kodier-Seite packt einen Blob, bevor er hineinkommt. Hier ist der Round Trip gegen SQLite über DBI und RSQLite:

library(DBI)
library(RSQLite)
db <- dbConnect(SQLite(), ":memory:")
dbExecute(db, "CREATE TABLE files (name TEXT, payload TEXT)")
stored <- base64encode(charToRaw("stored in a database"))
dbExecute(db, paste0("INSERT INTO files VALUES ('note.txt', '", stored, "')"))
row <- dbGetQuery(db, "SELECT * FROM files")
rawToChar(base64decode(row$payload))
#> [1] "stored in a database"

Die Alternative ist, die Bytes nativ als BLOB zu speichern, in dem Fall ist überhaupt kein Base64 nötig, und die Spalte kommt als raw vector zurück in R. Die Base64-in-TEXT-Variante existiert der Portabilität wegen: Sie können sie mit einem Texteditor inspizieren, diffen, und jede andere Sprache kann sie ohne Binär-Treiber lesen. Dasselbe Argument trägt sie in die Konfiguration hinein, wo ein Zertifikat oder ein Geheimnis als String in YAML, JSON oder einer Umgebungsvariable gespeichert ist:

Sys.setenv("API_CERT" = base64encode(charToRaw("LTSSECRET")))
Sys.getenv("API_CERT")
#> [1] "TFRTU0VDUkVU"

Eine Warnung gehört hierher, denn Konfigurationsdateien sind der Ort, an dem Geheimnisse leben: Base64 ist Kodierung, nicht Verschlüsselung. Ein Base64-Wert in einer Config-Datei ist für jeden lesbar, der die Datei lesen kann. Es überlebt Transport und Texteditoren; es schützt nichts.

E-Mail

E-Mail ist der Ort, an dem der 76-Zeichen-Umbruch geboren wurde, und MIME-Anhänge tragen ihn immer noch: Base64-Inhalt, umgebrochen bei 76 Zeichen mit CRLF zwischen den Zeilen, in einem Teil, der Content-Transfer-Encoding: base64 deklariert. Der Kodierer, der genau diese Form produziert, ist base64encode() mit seinen Umbruch-Argumenten auf den MIME-Vertrag eingestellt:

body <- "The quick brown fox jumps over the lazy dog, and then it came back down again."
mime_part <- base64encode(charToRaw(body), linewidth = 76, newline = "\r\n")
strsplit(mime_part, "\r\n", fixed = TRUE)[[1]]
#> [1] "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZywgYW5kIHRoZW4gaXQg"
#> [2] "Y2FtZSBiYWNrIGRvd24gYWdhaW4u"

R hat keinen erstklassigen Mail-Client, aber der Punkt steht überall da, wo Sie MIME-Teile von Hand bauen oder inspizieren, .eml-Fixtures generieren oder Anhänge aus einer herausparsen: Das ist die Form, die das Base64 haben muss, und die Decode-Seite dieses Artikel-Paares zeigt die nachsichtigen Dekodierer, wie sie es auf dem Weg zurück abwickeln.

Große Payloads und die String-Obergrenze

R-Strings haben eine harte Obergrenze von 2^31 - 1 Bytes, und weil die kodierte Form rund ein Drittel größer ist als das Original, würde eine Datei mit rund 1,5 GB Rohdaten ihre Ein-Zeilen-Kodierung über die Wand drücken. Der praktische Zug ist derselbe, den base64enc seit seinem Long-Vector-Release 2022 anbietet: die Ausgabe als Zeilen halten, nicht als einen String:

big <- raw(10 * 1024 * 1024)
packed <- base64encode(big, linewidth = 76)
length(packed)
#> [1] 183961
sum(nchar(packed))
#> [1] 13981016

Zehn Megabytes aus Nullen werden zu 183961 Zeilen von höchstens 76 Zeichen, ein vollauf gewöhnlicher Vektor, den Sie Zeile für Zeile schreiben oder durch eine Pipe streamen können, ohne je einen riesigen String zu halten. Für den Data-Frame-Fall, eine Spalte mit vielen binären Werten, ist b64 der Geschwindigkeits-Champion: Seine Rust-Engine kodiert die ganze Spalte in einem vektorisierten Aufruf, was ein dramatischer Unterschied zum Loopen pro Zeile ist. Wenn Sie eine Spalte aus kodierten Werten produzieren müssen, führen Sie selbst einen schnellen system.time()-Vergleich durch; die Lücke zwischen einem Zeilen-Loop und einem vektorisierten Aufruf ist meist groß genug, dass sie sich auswirkt.

Die Kommandozeile

Nicht alles braucht eine volle R-Session. Das klassische Unix-Tool spricht Base64 nativ, kodiert ohne Flags auf jeder Plattform und dekodiert mit -d auf Linux und -D auf macOS und den BSDs:

echo -n "Hello, world!" | base64
#> SGVsbG8sIHdvcmxkIQ==
echo -n "SGVsbG8sIHdvcmxkIQ==" | base64 -d
#> Hello, world!

Beachten Sie das -n in der ersten Zeile: Ohne es steuert echo einen Zeilenumbruch bei, und die Ausgabe kodiert 14 Bytes statt 13 und endet in o= statt IQ==. Ein GNU-base64 umwickelt auch für Sie (-w 76), was praktisch ist, wenn Sie in etwas pipen, das MIME-förmige Eingabe erwartet. Und ein einzeiliger Rscript erledigt dieselbe Arbeit mit denselben Paketen, die Sie in Ihren Skripten verwenden:

Rscript -e 'library(base64enc); writeLines(base64encode(charToRaw("Man")))'
#> TWFu

Nutzen Sie die Shell für schnelle Prüfungen und Pipes, und R, wenn das Ergebnis in einem Data Frame, einer Datei oder einem Report leben muss. Und fügen Sie keine Mehr-Megabyte-Strings in das Terminal ein: Kommandozeilen-Argumente stoßen lange vor Base64 an ARG_MAX, also pipen Sie stattdessen über eine Datei.

Fallen, die sich zu kennen lohnen

Hier ist die kurze Liste der Wege, wie die Kodier-Seite R-Entwickler beißt, und alle sind dem Ökosystem eigen und nicht Base64 im Allgemeinen:

  • Ein String ist ein Dateiname. base64encode("Man") versucht, eine Datei namens Man zu öffnen. Die Warnung benennt die Datei; der Fehler sagt, die Verbindung sei fehlgeschlagen. Verwenden Sie bei base64encode() zuerst charToRaw().
  • Leere Eingabe wird auf drei Arten kodiert. base64encode(raw(0)) gibt character(0) zurück, während b64::encode("") und base64url::base64_urlencode("") "" zurückgeben. Folge-Code für Strings erwartet keinen Vektor der Länge null.
  • openssl umbricht und fügt dann eine Geister-Zeile hinzu. linebreaks = TRUE bricht bei 64 mit LF ab und hängt einen Zeilenumbruch am Ende an, den strsplit() still verwirft, so dass naive Zeilen- und Zeichenzählungen sich um eine Zeile nicht einig sind.
  • Umbrüche sind ein Vertrag. MIME ist 76 mit CRLF, PEM ist 64, JSON-APIs wollen meist gar nichts. Wählen Sie die Form, die die andere Seite erwartet, denn ein Dekodierer, der einen Umbruch-Stil toleriert, lehnt einen anderen ab.
  • Das URL-sichere Alphabet muss an beiden Enden passen. "----", URL-sicher kodiert, scheitert in einem Standard-Dekodierer am ersten Strich, und Padding wird zum %3D, in dem Moment, in dem der String in einer URL lebt.
  • b64_chunk verlangt Vielfache von vier. Jede andere Breite erntet Chunk size must be a multiple of 4., denn eine Base64-Gruppe kann nicht in der Mitte durchgeschnitten werden.
  • Ein Zeilenumbruch am Ende kann den Dekodierer zum Panic bringen. Dateien, die Sie für b64::decode_file() zum Lesen schreiben, müssen ohne Zeilenumbruch enden; cat(), nicht writeLines().
  • JWT-Zeit-Claims werden erzwungen. jwt_decode_hmac() verweigert abgelaufene Tokens und nbf-Claims in der Zukunft, und httr2 maskiert die jwt_*-Funktionen von jose, wenn Sie es als Zweites laden.
  • Der Payload ist sichtbar. Base64-Claims in einem JWT, einem Data-URI oder einer Config-Datei sind für jeden lesbar. Kodierung ist keine Verschlüsselung.
  • Die String-Obergrenze ist eine Wand, keine Empfehlung. Rund 1,5 GB Rohdaten pro R-String ist der Punkt, an dem eine Ein-Zeilen-Kodierung nicht mehr passt; halten Sie große Ausgabe als Zeilen oder streamen Sie sie.

Best Practices

  • Bei base64enc::base64encode() zuerst mit charToRaw() umwandeln, bevor Sie kodieren. Wenn Sie direkte Character-Eingabe und Vektorisierung brauchen, ist b64::encode() das Paket, das Strings als Strings behandelt.
  • Bei Alltagsarbeit zu base64enc::base64encode() greifen, zu b64 greifen, wenn Sie Geschwindigkeit, echte Vektorisierung oder die URL-sicheren Engines wollen, und openssl nutzen, wenn es bereits im Projekt ist und Sie PEM-förmige Ausgabe wollen.
  • Die Umwicklung pro Kanal wählen, nicht pro Paket: nichts für JSON und URLs, 76 mit CRLF für MIME, 64 für PEM-artige Blöcke, und konsequent bleiben, damit die Decode-Seite der Pipe weiß, was sie erwarten soll.
  • Das URL-sichere Alphabet ohne Padding für alles benutzen, was in einer URL, einem JWT oder einem Dateinamen leben wird, und das Standard-Alphabet für E-Mail.
  • Bei JWTs exp (und iat) explizit setzen, mit jwt_decode_hmac() verifizieren, bevor Sie einem Token trauen, und daran denken, dass jeder Claim öffentlicher Text ist.
  • Ihre Kodier-Pfade mit einem Round Trip testen, identical(text, rawToChar(base64decode(base64encode(charToRaw(text))))); das ist eine Zeile, und sie fängt Alphabet-, Padding- und Zeichensatz-Fehler auf einen Schlag.
  • Für Dateien b64::encode_file() oder base64::encode() bevorzugen, statt die ganze Datei in einen String zu lesen, und Ihre Ausgabe mit cat() schreiben, wenn ein strenger Dekodierer sie lesen wird.
  • Das Packband ehrlich halten: Base64 bringt Bytes ans Reisen, es macht sie nicht geheim und nicht kleiner. Erst verschlüsseln, wenn Geheimhaltung das Ziel ist, erst komprimieren, wenn Größe.

Wie Base64 nach R kam

Das Format kam lange davor, dass R damit etwas anfangen konnte. Es wurde 1987 für das Privacy-Enhanced-Mail-Protokoll standardisiert (RFC 989), 1993 von MIME übernommen (RFC 1521, dann der finale RFC 2045 von 1996, der den 76-Zeichen-Umbruch immer noch definiert), 2003 in RFC 3548 geordnet und 2006 in RFC 4648 in seine moderne Form gebracht, die das URL-sichere Alphabet, die no-padding-Option und den kleineren Bruder Base32 hinzufügte. Die R-Geschichte begann im September 2012, als Simon Urbaneks base64enc auf CRAN landete und sich leise zu der Standard-Antwort auf "Wie Base64 ich das" für über ein Jahrzehnt entwickelte, mit checkUTF8() seit 2015 und der Unterstützung für lange Vektoren seit 2022. Die Krypto-Welt kam über openssl herein, Jeroen Ooms' langjähriger Wrapper um das System-OpenSSL, dessen base64_encode() seitdem die PEM-förmige Option ist. Das alte base64-Paket, ebenfalls von Ooms, wurde im Oktober 2024 ausdrücklich als Kompatibilitäts-Wrapper neu herausgegeben, seine eigene Beschreibung verweist jetzt neue Anwendungen woanders hin. Dann kam b64 im Januar 2024, eine Rust-Engine, gebaut mit extendr, die Vektorisierung und einen Stab voller Alphabete brachte, und im April 2026 veröffentlichte jose Version 2.0, die der jwt_*-API ED25519-Unterstützung hinzufügte und signierte Tokens zu einem First-Class-Citizen machte. Das Ergebnis ist eine Werkzeugkiste mit genau einem Kodierer pro Job: Alltags-Strings, PEM-Blöcke, Geschwindigkeit, URLs, Dateien und Tokens.

Schöne Fakten, R-Edition

Denn ein vollständiger Leitfaden sollte mit einem Lächeln enden:

  • Jedes Base64-PNG im Internet beginnt mit denselben Zeichen: Der magische Kopf 89 50 4E 47 kodiert zu iVBOR, so dass das Greppen einer HTML-Datei nach diesem Fingerabdruck jedes eingebettete Bild findet. Formate haben Fingerabdrücke, und dieser hier ist ein Präfix.
  • base64encode("Man") kodiert nicht das Wort Man. Es macht sich auf die Suche nach einer Datei namens Man, warnt, dass es sie nicht öffnen kann, und gibt auf. Die mit Abstand R-spezifischste Falle im Base64-Ökosystem, offen vor allen Augen in der Argumentliste versteckt.
  • Ein OpenSSL-umwickelter String endet immer mit einem Zeilenumbruch, also ist die letzte Zeile eines PEM-Blocks nie die letzte Zeile der Datei. Der Umbruch hat einen Punkt am Satzende, ob Sie ihn wollen oder nicht.
  • Der leere String wird auf drei verschiedene Arten kodiert: base64enc gibt einen Vektor aus null Strings zurück, b64 und base64url geben den leeren String zurück. R trifft auf nichts, und R gibt drei Antworten.
  • jose schreibt den JWT-Header mit typ vor alg, während die meisten von Hand geschriebenen Beispiele alg zuerst setzen. JSON schert sich nicht um die Schlüsselreihenfolge, und die JWT-Verifikation weiß es auch, aber Ihr String-Diff nicht.
  • MIMEs 76-Zeichen-Limit ist eine Entscheidung aus 1993 über die Zeilenlänge von Nachrichten, die durch vier RFCs in jeden E-Mail-Anhang getragen wurde, den Sie je gesendet haben. Der Umbruch, den Sie heute einstellen, wurde überstritten, bevor R einen Farb-Plot hatte.
  • b64 wird Alphabete dekodieren, die Sie nie gesehen haben: BinHex, IMAP modifiziertes UTF-7, bcrypt, crypt und das URL-sichere Paar, jeweils mit einer Engine. Derselbe Rust-Code, der Ihr JSON-Feld packt, kann einen Macintosh-Anhang aus den 1980ern auspacken.

Zusammenfassung

Wählen Sie Ihren Kodierer nach dem Job: base64enc für Alltags-Strings, mit linewidth und newline, wenn der Kanal eine Form hat; openssl, wenn Sie PEM-artige 64-Zeichen-Ausgabe wollen oder es bereits im Projekt ist; b64, wenn Sie Geschwindigkeit, Vektoren oder die URL-sicheren Engines wollen; und die kleinen Spezialisten base64 und base64url für Datei-Kleinigkeiten und URL-sichere Strings. Mit charToRaw() umwandeln, bevor Sie mit base64enc::base64encode() kodieren, die Umwicklung pro Kanal statt pro Paket wählen, das URL-sichere Alphabet für alles behalten, was in einer URL oder einem JWT leben wird, Tokens mit jose signieren und verifizieren, und jeden Pfad mit einem Round Trip testen. Das Format selbst ist an genau zwei Orten ungnädig, dem Alphabet und den Zeilenumbrüchen, und die Kodierer unterscheiden sich vor allem darin, wie ehrlich sie Ihnen sagen, wenn Sie einen der beiden vermasselt haben. Und wenn die andere Richtung ruft - wenn ein String ankommt und Sie ihn zerlegen müssen, prüfen, was die Bytes bedeuten, und die Dekodierer überstehen, die still scheitern - behandelt der Schwesteraartikel Base64-Dekodierung in R im Detail.

Zuletzt aktualisiert: 2026-09-08

Verwandter Artikel: Base64-Dekodierung in R: Ein vollständiger Leitfaden