Base64 形式を扱う必要がありますか?それならこのサイトが最適です!データをエンコードまたはデコードするために便利なオンラインツールをご利用ください。

R での Base64 エンコード:完全ガイド

旅に出る必要があるバイトがあって、その道はテキストしか許さない。JSON フィールドの中に住まなければならない JPEG。環境変数に入っていなければならない証明書。自己完結型の HTML レポートの中で旅をしなければならないプロット。Base64 はそれらすべてへの梱包テープです:どんなバイト列でも、64 個の無害な文字の文字列になって、投げかけられるどんなテキストチャネルでも生き延びます。このサイトのホームページではフォーマットを完全に扱っているので、短いバージョンはこうです:3 バイトが入り、4 文字が出ます。A から Z、a から z、0 から 9 に + と / を加えた中から選び、積荷がぴったり割れないときは末尾に少々の = パディングがつきます。

R 特有のひねり:base R には Base64 エンコーダーがまったくありません。base パッケージに base64_encode() が待機していることも、1行で使えるビルットインもあることもありません。パッケージを選ぶのですが、エコシステムは本物に選択肢をくれます。速さも違えば、折り返し癖も、パディングに対する意見も違います。この記事の終わりまでに、どんな状況でどのエンコーダーに手を伸ばせばよく、どのエンコーダーが黙ってエンコード以外のことをするか、分かるようになります。

エンコーダーの風景

エンコードをこなすのは5つのパッケージで、日々の主力、暗号の隣人、そして小さな専門家に分かれます。これが出演者たちの顔ぶれで、2026年時点のものです:

パッケージ バージョン(2026) エンコードのエントリポイント 折り返し癖 手に取る場面
base64enc 0.1-6 base64encode() linewidth と newline、すべてあなたの指定のまま 日常の文字列、MIME 折り返し
openssl 2.4.2 base64_encode() 64 文字の行、LF 改行、末尾の改行付き PEM ファイル、既存の暗号スタック
b64 0.1.7 encode(), encode_file() 折り返さない;必要なときに b64_chunk() と b64_wrap() 速さ、ベクトル、URL 安全なエンジン
base64 2.0.2 encode() デフォルトで 64 文字の行に末尾改行 ファイルからファイルへの雑用、レポートの画像
base64url 1.4 base64_urlencode() 折り返さず、パディングなし、文字列入力 URL 安全な文字列

もう3つのエンコーダーが、あなたがすでに読んでいるかもしれないパッケージの中に隠れています。jsonlite は base64_enc() と base64url_enc() をエクスポートしているので、すでに JSON をパースしていれば、手元にエンコーダーがあるかもしれません。jose は JWT 作業用の base64url_encode() をエクスポートしています。そしてベテランの RCurl パッケージは、きちんと動いてはいるものの前時代的なものに属する、libcurl を包んだ base64() ラッパーを今も抱えています。最後に base64 パッケージは、ラベルの上で自分を互換ラッパーとして説明しており、新規アプリケーションを base64enc、openssl、jsonlite へと向けています。

セットアップ

まだマシンに R が入っていなければ、オペレーティングシステムが用意しています:Debian と Ubuntu では r-base、Fedora では R、macOS では Homebrew か公式インストーラー、Windows ではインストーラー。その次はエンコーダーで、CRAN からまっすぐ:

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

インストールが横滑りする2つの場所はどちらもビルド時です。openssl はシステムの OpenSSL に対してコンパイルされるので、なま物の Linux マシンは先に開発用ヘッダーを要求します(sudo apt install libssl-dev)。あるいは Debian と Ubuntu では sudo apt install r-cran-openssl でコンパイル自体を丸ごとスキップできます。そして b64 は extendr で包んだ Rust エンジンのため、ソースビルドには Rust ツールチェーンが必要です(sudo apt install cargo を実行すると rustc も一緒に入ります)。Windows と macOS では CRAN からプリビルド済みバイナリが得られるので、これらは一切当てはまりません。

最初のエンコード

エンコードの日常の90%は3行で収まります。デコード側がスモークテストとして使うのと同じ、あの有名な文字列を使ったものです:

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

あの儀式で注目すべきことが3つあります。第一に、入力は raw ベクトルです:charToRaw() は R の文字列から詰められるバイトへの橋であり、この表の日常使いのエンコーダー - base64enc、openssl、b64 - はすべて raw を受け取ります。第二に、出力はデコーダーとは逆の方向、つまり単一の 文字列です。エンコードは境界のテキスト側で終わるからです。第三に、長さの計算が動くところを見てください:3 バイトが入り、4 文字が出て、積荷がぴったり割れるのでパディングは不要です。割り切れないときは、1つか2つの = が末尾に着地します。

そして信頼できないエンコーダーは無いほうがマシなので、2つの方向が一致することを証明するラウンドトリップがこれです:

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

文字列、バイト、そしてファイル名の罠

さて、罠です。R 開発者は全員が一度はこれに落ちます。base64encode() は character 引数をエンコードする文字としてではなく、ファイル名として扱います。文字列を渡すと、そのファイルを探しに出かけます:

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"

警告が手がかりです:file("Man", "rb") を試みたのです。「Man というファイルを raw 読み取り用に開く」の意味です。なので base64encode() では、規律とは1つの反射です:常に先に charToRaw() を。本当にファイルが意図なら、その関数がやっているのがそれで、出力は詰められたファイルのバイトであり、それはときどきまさに望むものなのです。

b64 は同じ問いに対して別の立場を取ります:その encode() は文字列ベクトルを直接受け取り、各要素を UTF-8 テキストとして扱い、しかもベクトル化されています:

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

この境界のもう2つの端も知っておく価値があります。空の入力は、誰に聞くかによって3つの異なる方法でエンコードされます:

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

base64enc は空の文字列ではなく長さゼロの文字列ベクトルで答えるので、文字列を期待して character(0) を受け取った下流のコードは、驚くべき場所で失敗します。そして文字セットの決定はエンコード側にも住んでいます:charToRaw() は文字列をその文字列が現在身につけているエンコーディングで詰め込むので、UTF-8 テキストは UTF-8 バイトとして旅し、それが線のもう一端が期待するものです。イモジも含めて、最新の R は U+FFFF 以上のコードポイントを本物の UTF-8 として保存するので:

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

行折り返し:MIME、PEM、そして自分だけの幅

長い Base64 文字列は行に折り返されます。世界中で最も古いテキストチャネルには列数制限があり、MIME はそれをいつまでも忘れていないからです。出会う歴史的な折り返しは2つで、MIME は行間に CRLF を挟んで 76 文字で折り返し、PEM は 64 で折り返します。すべてのエンコーダーには、どちらを使うか、あるいは使わないかの独自の見解があるため、これはエンコード前に契約を選ぶセクションです。

まずサイズの計算から。折り返しとは、出力がどれほど長くなるかを知ることだからです。3 バイトごとに4文字が出るので、エンコード後の形式の長さはシンプルな天井です:

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

それをポケットに詰めたら、エンコーダーたち。base64enc が最も柔軟です:デフォルトでは切れ目のない1行を出力し、linewidth 引数を渡すと行のベクトルを返します:

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

幅 76 で 100 バイトが2行。デフォルトではベクトルで、newline を足すと CRLF で結ばれた単一の文字列になります。末尾の空要素はありません:ちょうど2行の 76 文字にエンコードされる 114 バイトは、3行ではなく2行として返ってきます。

openssl は反対の極を取ります。linebreaks = TRUE は 64 文字で素の LF 改行を使って折り返し、最後に改行を1つ追加します:

wrapped <- openssl::base64_encode(charToRaw(strrep("a", 100)), linebreaks = TRUE)
nchar(wrapped)
#> [1] 139          # データ文字 136 文字 + 改行 3 個
nchar(strsplit(wrapped, "\n", fixed = TRUE)[[1]])
#> [1] 64 64 8

計算を数えてみましょう:データ文字 136、内部の改行 2、末尾の改行 1、合計 139。そしてその末尾の改行は、あなたが書ける最も自然な行数カウントには見えません。strsplit() が末尾の空の一片を落とすからです。ベクトルは3行と言うのに、文字列は4行を載せています。OpenSSL の折り返し出力と MIME の折り返し出力を diff して文字数が合わないとき、これがゴーストです。

b64 はまったく折り返しません。2つの操作を別々に与え、チャンク幅には1つのルールがあります:4 の倍数であること。エンジンが Base64 グループを半分に切るのを拒否するからです:

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.

そしてファイル志向の base64 パッケージは OpenSSL に倣います:64 文字の行、末尾の改行、デフォルトでオン:

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

最後のゼロは末尾の改行で、readLines() が空の最終行として拾っています。ここに全戦場を一度に:

エンコーダー 幅 改行 末尾の改行
base64encode(x) 1行 なし なし
base64encode(x, linewidth = 76, newline = "\r\n") 76 CRLF なし
openssl::base64_encode(x, linebreaks = TRUE) 64 LF あり
b64::encode(x) 1行 なし(b64_chunk() と b64_wrap() で折り返し) なし
base64::encode(in, out) 64 LF あり
base64url::base64_urlencode(x) 1行 なし なし

URL 安全な Base64

標準 Base64 はアルファベットの最後の2枠を + と / に使い、それはまさに URL が嫌がる文字です:プラスは %2B に、スラッシュは %2F に、パディングの各 = は %3D になります。RFC 4648 セクション 5 で定義される URL 安全な変体は、その2文字を - と _ に交換し、たいていパディングを捨てるので、どこにでも貼れるはずのトークンがどこにでも貼れるままになります。R にはそこへの扉が3つあります。

b64 のエンジンが最も完全です:エンジンは設定済みのアルファベットとパディング方針で、パッケージは必要な4つを同梱しています。同じ3バイトを標準エンジンと URL 安全なエンジンにそれぞれ渡して、アルファベットが仕事を果たす様子を見てください:

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"

エンジンは4つ、"standard"、"standard_no_pad"、"url_safe"、"url_safe_no_pad" で、同じエンジンオブジェクトが両方向で動くので、コードは対称のまま保てます。ノンパディング変種は、パディングが実際に現れる場合にだけ違って見えます。上の1バイトの例のように。

専用の base64url パッケージが単一目的の扉です:文字列入力、URL 安全な文字列出力、折り返さず、パディングもしない:

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

そして jose は JWT 作業用の自分専用 base64url_encode() をエクスポートし、デコード側は望む通り raw ベクトルを返します。3つの扉すべてを束ねる1つのルール:エンコードに使ったアルファベットは、デコードに使うべきアルファベットです。標準デコーダーに文字列 "----" を渡すと、最初のダッシュで失敗します。汚れた入力や食い違った入りに各デコーダーがどう出会うかは、デコードの姉妹記事で扱います。

JWT:トークンの作成

JSON Web Token は、URL 安全な Base64 が日常的な道具になった場所です。JWT はドットで結ばれた3つの base64url パートからなります:それがどう署名されたかを説明するヘッダー、JSON クレームのペイロード、そして2つを結びつける署名。エンコード側では3つすべてをあなたが組み立て、jose パッケージが儀式全体をこなします。その API は jwt_* 関数(jwt_claim()、jwt_encode_hmac()、jwt_decode_hmac()、jwt_split())を中心に作られており、2.0 リリース(2026年4月)は ED25519 サポートを追加し、typ ヘッダーフィールドを任意にしました:

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"

jwt_claim() が背景で何をしたかに注目してください:iat(発行時刻)はデフォルトで現在の時刻なので、求めもされていないのにペイロードに現れました。一方、exp はデフォルトが何もなく、つまり期限のないトークンという意味で、それはたいてい望むものではありません。exp を意図的に設定し、残りは署名が引き受けます。jwt_split() は検査ツールです:ヘッダー、名前付きリストとしてのペイロード、raw の署名が手元に戻り、検証は伴いません。まさにこの記事ペアのデコード側が説明する覗き込みのステップそのものです。

検証側こそ jose が本領を発揮する場所です。jwt_decode_hmac() は署名をチェックし、時刻クレームを強制します。そしてその拒否の仕方は具体的です:

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

成功するとクレームは普通のリストとして戻ってくるので、round$sub と仲間はそのまま動きます。将来の nbf(以前には無効)クレームには独自の拒否があり Token is not valid before ...、HMAC トークンは非対称の jwt_decode_sig() ではデコードされず、それは Unsupported algorithm: HMAC と答えて、代わりに公開キーを要求します。最後の注意を2つ。第一に、ペイロードはエンコードされ、暗号化されたものではありません:すべてのクレームは誰にでも読めるので、秘密なものはそこに属しません。第二に、jose のあとに httr2 を読み込むと、マスキングのメッセージに注意してください:httr2 は OAuth クライアントクレデンシャル用に作られた、exp がデフォルトで5分先の、自分専用の jwt_claim()、jwt_encode_hmac()、jwt_encode_sig() をエクスポートし、セッションの残り時間で jose 版を隠してしまうのです。

ファイル:バイナリ入力、テキスト出力

ファイルのエンコードはデコード側のファイル作業の鏡像であり、b64 パッケージが最も明快なエントリポイントを備えています。これがまた最速で、1つの巨大な中間文字列を一切作り上げないからです:

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"

あの警告は変装した機能です:cat() は末尾の改行を追加しないので、ファイルは行の途中で終わり、readLines() がそう教えてくれます。この事実のどちらの側にいるかを覚えておいてください。ファイルが末尾改行でパニックになる b64::decode_file() に消費されるかもしれないからです。デコードの記事に全貌があります。cat() か writeBin(charToRaw(enc), path) で書けば、あの刃は钝いままです。

base64 パッケージは純粋なファイルからファイルへの選択肢で、対応する関数のペアと、OpenSSL 風の折り返しがデフォルトです:

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

出力パスを返すので、ログにとって便利です。そして機構を見たいとき、手動パイプラインは表のどのエンコーダーでも動きます:ファイルを raw として読み、エンコードし、テキストを書く、完了:

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: URI と自己完結型ドキュメント

data: URI スキーム(RFC 2397)はエンコード側で最も目に見える利用者です:自身のコンテンツ、MIME タイプ、「base64」という単語、そしてペイロードを、1つの属性に全部載せていくドキュメントです。PNG のマジックバイトがこのパターンを認識させます:現実のあらゆる Base64 PNG は同じ文字で始まります。ファイルヘッダー 89 50 4E 47 は常に同じようにエンコードされるからです:

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, "\" />")

埋め込み画像を探すために HTML ファイルを iVBOR で grep したことがあるなら、この指紋が機能する理由はそれです。R Markdown レポート、単一ファイルのダッシュボード、スクレイプしたページはすべて同じ形を使い、1つを作るのも同じパターンです:ファイルを raw として読み、エンコードし、MIME 接頭辞の後ろに貼る。コストは先ほどのサイズ計算で、元の約3分の1大きく、あなたの HTML の中に永遠に座り続けるので、埋め込み画像はすっきりと保ちましょう。

API と Web リクエスト

この記事ペアのデコード側はあなたに Base64 を手渡す API に出会いますが、この側はそれを要求する API に出会います。パターンは毎回同じです:バイトをエンコードし、その文字列を JSON ボディに入れて、送る。現代的な HTTP クライアントは 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

道中のための注意が3つ。リクエストのコンストラクタは request() で、httr2 の最初のリリースからずっとその名前です。レスポンスを読みるとき、ボディは raw ベクトルで到着するので、パース前に rawToChar()。そして API は方言を話すこともあります:URL 安全なアルファベットを要求するものもあれば、パディングの剥がしを要求するものもあります。JSON API は文字列値の中の = に何も感じないので、パディングの問いが URL の問いになるのは、Base64 がパスやクエリで旅するときだけです。分からなければ、頭の中の仕様書ではなく、API の例を読みましょう。

データベース、設定、環境

データベースの Base64 は、テキスト列を縫い抜けて密輸されてきたバイナリで、エンコード側は中に入る前にブロブを詰め込むことです。ここは DBI と RSQLite を経由した SQLite でのラウンドトリップです:

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"

もう一つの方法は、バイトをネイティブに BLOB として保存することです。その場合 Base64 はまったく不要で、列は raw ベクトルとして R に戻ってきます。TEXT 中の Base64 という変種が存在するのは移植性のためです:テキストエディタで中身を見られ、diff も取れ、他のどんな言語でもバイナリドライバなしで読めます。同じ主張が設定までそれを運びます。証明書やシークレットが YAML、JSON、環境変数に文字列として保存される場所です:

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

1つの注意をここに置きましょう。設定ファイルこそシークレットが住む場所だからです:Base64 はエンコードであり、暗号化ではありません。設定ファイルの Base64 値は、そのファイルを読める誰にでも読めます。輸送もテキストエディタも生き延びますが、何も守りません。

メール

メールこそ、76 文字の折り返しが生れた場所で、MIME の添付ファイルは今もそれを身につけています:Base64 のコンテンツが行間に CRLF を挟んで 76 文字で折り返され、Content-Transfer-Encoding: base64 を宣言するパートの中に入っているのです。ちょうどその形を作るエンコーダーは、折り返し引数を MIME の契約に設定した base64encode() です:

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 に第一級のメールクライアントはありませんが、MIME のパートを手で組んだり点検したり、.eml のフィクスチャを生成したり、そこから添付ファイルをパースしたりするたびに、ポイントは変わりません:これが Base64 が持つべき形で、この記事ペアのデコード側が寛容なデコーダーが帰路でそれを解きほぐす様子を示します。

大容量ペイロードと文字列の上限

R の文字列には 2^31 - 1 バイトという硬い上限があり、エンコード後の形式は元より約3分の1大きくなるので、生のデータがだいたい 1.5 GB のファイルは、1行エンコードで壁を越えてしまいます。実用的な動きは、base64enc が 2022 年の長ベクトルリリース以来提供してきたものと同じです:出力は1つの文字列ではなく、行のままにしておくこと:

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

ゼロ 10 メガバイトは、76 文字までの 183961 行になり、行ごとに書き出してもパイプでストリームしても、巨大な文字列を1つ抱え続けることなく扱える、ごく普通のベクトルです。データフレームの場合、つまり多くのバイナリ値の列では、b64 がスピードチャンピオンです:その Rust エンジンは列全体を1回のベクトル化呼び出しでエンコードし、行ごとにループするのとは桁違いです。エンコード値の列を生成する必要があれば、自分で簡単な system.time() 比較を実行してみてください。行ごとのループと1回のベクトル化呼び出しの差は、たいていそれだけの価値があります。

コマンドライン

すべてにフルの R セッションが必要というわけではありません。クラシックな Unix ツールは Base64 をネイティブで話し、エンコードは全プラットフォームでフラグなし、デコードは Linux では -d、macOS と BSD では -D です:

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

最初の行の -n に注目してください:それがないと echo が改行を1つ寄与し、13 バイトではなく 14 バイトがエンコードされて、IQ== の代わりに o= で終わります。GNU の base64 は代わりに折り返しもしてくれます(-w 76)。MIME 風の入力を期待するものへパイプするときに便利です。そして1行の Rscript が、スクリプトで使っている同じパッケージで同じ仕事をしてくれます:

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

素早い確認やパイプにはシェルを使い、結果がデータフレーム、ファイル、レポートの中に住む必要があるときには R を使ってください。そして何メガバイトもの文字列をターミナルに貼り付けたりしないでください:コマンドライン引数は Base64 よりずっと早く ARG_MAX にぶつかるので、代わりにファイル経由でパイプ送りにしてください。

知っておきたい落とし穴

エンコード側が R 開発者を噛む仕方の短いリストです。すべては Base64 全般ではなく、エコシステム自体に根ざしたものです:

  • 文字列はファイル名です。 base64encode("Man") は Man というファイルを開こうとします。警告はファイル名を名乗り、エラーは接続の失敗を言います。base64encode() では先に charToRaw() を使ってください。
  • 空の入力は3つの方法でエンコードされます。 base64encode(raw(0)) は character(0) を返し、b64::encode("") と base64url::base64_urlencode("") は "" を返します。下流の文字列コードは長さゼロのベクトルを期待していません。
  • openssl は折り返してからゴースト行を追加する。 linebreaks = TRUE は LF で 64 に折り返し、末尾の改行を付け足しますが、strsplit() はそれを無言で落とすので、素朴な行数と文字数が1行だけ食い違います。
  • 折り返しは契約です。 MIME は CRLF で 76、PEM は 64、JSON API はたいてい何も望みません。相手の側が期待する形を選びましょう。ある折り返しを許容するデコーダーは、別の折り返しを拒否します。
  • URL 安全なアルファベットは両端で一致させなければならない。 URL 安全にエンコードされた "----" は標準デコーダーで最初のダッシュで失敗し、文字列が URL の中に住んだ瞬間、パディングは %3D になります。
  • b64_chunk は4の倍数を要求する。 他のどんな幅も Chunk size must be a multiple of 4. を受け取ります。Base64 のグループは半分に切れないからです。
  • 末尾の改行はデコーダーをパニックにさせうる。 b64::decode_file() に読ませるファイルは、改行なしで終わらなければなりません:writeLines() ではなく cat()。
  • JWT の時刻クレームは強制される。 jwt_decode_hmac() は期限切れトークンと将来の nbf クレームを拒否し、httr2 を2番目に読み込むと、jose の jwt_* 関数をマスキングします。
  • ペイロードは見えます。 JWT、data: URI、設定ファイルの Base64 クレームは誰にでも読めます。エンコードは暗号化ではありません。
  • 文字列の上限は指針ではなく壁です。 R の文字列1つあたり約 1.5 GB の生データで、1行エンコードははまりきらなくなります。大きな出力は行のままにするか、ストリームにしてください。

ベストプラクティス

  • base64enc::base64encode() を使うときは、エンコード前に charToRaw() で変換してください。直接の文字列入力とベクトル化が必要なら、b64::encode() が文字列を文字列として扱うパッケージです。
  • 日常の仕事では base64enc::base64encode() を既定にし、速さ、真のベクトル化、URL 安全なエンジンが欲しいなら b64 に手を伸ばし、PEM 風の出力が欲しく、openssl がすでにプロジェクトにあるなら openssl を使いましょう。
  • 折り返しはパッケージではなくチャネルごとに選んでください:JSON と URL にはなし、MIME には CRLF で 76、PEM 風ブロックには 64。そして一貫性を保ち、パイプのデコード側が何を期待すればよいか分かるようにしてください。
  • URL、JWT、ファイル名に生きるものにはすべて、パディングなしの URL 安全なアルファベットを、メールには標準アルファベットを使いましょう。
  • JWT では exp(と iat)を明示的に設定し、トークンを信頼する前に jwt_decode_hmac() で検証し、すべてのクレームが公開テキストであることを忘れないでください。
  • エンコードパスはラウンドトリップでテストしてください:identical(text, rawToChar(base64decode(base64encode(charToRaw(text)))))。1行で、アルファベット、パディング、文字セットの誤りを一気に捉えてくれます。
  • ファイルでは、ファイル全体を1つの文字列に読むより b64::encode_file() か base64::encode() を選び、厳格なデコーダーが読む場合は出力を cat() で書きましょう。
  • 梱包テープを誠実に保ちましょう:Base64 はバイトを旅をさせるだけで、秘密にもせず、小さくもしません。秘密が目的なら先に暗号化し、サイズが目的なら先に圧縮してください。

Base64 は R にどうやってやってきたか

この形式は、R がそれに何かがするはるかに前にやってきました。1987 年に Privacy-Enhanced Mail プロトコル向けに標準化され(RFC 989)、1993 年に MIME が採用し(RFC 1521、その後 1996 年の最終版 RFC 2045。76 文字の折り返しは今もここで定義されています)、2003 年に RFC 3548 で整理され、2006 年の RFC 4648 で現代的な形が与えられました。そこには URL 安全なアルファベット、ノンパディングの選択肢、そして小さな兄弟 Base32 が加わりました。R の物語は 2012 年9月に始まります。Simon Urbanek の base64enc が CRAN に着き、10年以上にわたって「これをどうやって Base64 にするか」の既定の答えを静かに担ったからです。2015 年に checkUTF8() を、2022 年に長ベクトルサポートを勝ち取りました。暗号の世界は openssl 経由で入ってきました。システムの OpenSSL 周りの Jeroen Ooms の長年のラッパーで、その base64_encode() はそれ以来ずっと PEM 風の選択肢です。同じく Ooms による古い base64 パッケージは 2024 年10月に、明示的に互換ラッパーとして再発行され、その自己紹介は今や新規アプリケーションを他所へと導きます。そして 2024 年1月に b64 が登場。extendr で作られた Rust エンジンで、ベクトル化とアルファベットの安定軍団をもたらしました。2026 年4月には jose がバージョン 2.0 をリリースし、jwt_* API に ED25519 サポートを加え、署名されたトークンを第一級の市民にしました。その結果は、仕事ごとに1つのエンコーダーを持つ工具箱です:日常の文字列、PEM ブロック、速さ、URL、ファイル、そしてトークン。

豆知識コーナー

完全なガイドは笑顔で終わるべきですから:

  • インターネット上のあらゆる Base64 PNG は同じ文字で始まります:マジックヘッダー 89 50 4E 47 は iVBOR にエンコードされるので、HTML ファイルをその指紋で grep すればすべての埋め込み画像が見つかります。形式には指紋があり、これは前接辞です。
  • base64encode("Man") は単語 Man をエンコードしません。Man という名前のファイルを探しに出かけ、開けないと警告し、諦めます。Base64 エコシステムで最も R 固有の落とし穴が、引数リストの中に堂々と隠れています。
  • OpenSSL 折り返しの文字列は常に末尾の改行で終わるので、PEM ブロックの最終行はファイルの最終行ではありません。折り返しは、望もうが望むまいが文の末尾にピリオドを打つのです。
  • 空文字列は3つの異なる方法でエンコードされます:base64enc はゼロ個の文字列のベクトルを返し、b64 と base64url は空の文字列を返します。R はなにものかに会い、R は3つの答えを返すのです。
  • jose は JWT ヘッダーを typ が alg の前に来るように書きますが、ほとんどの手書きの例は alg を先頭に置きます。JSON はキーの順序を気にせず、JWT の検証はそれを分かっていますが、あなたの文字列 diff は分かりません。
  • MIME の 76 文字制限は、メッセージ行の長さについての 1993 年の決定で、4つの RFC を経由し、あなたが今まで送ってきたあらゆるメールの添付ファイルに乗っています。今日あなたが設定している折り返しは、R にカラープロットが生まれる前に論争されたものです。
  • b64 はあなたが一度も見たことのないアルファベットをデコードできます。BinHex、IMAP modified UTF-7、bcrypt、crypt、URL 安全ペアで、それぞれに1つのエンジン。JSON フィールドを詰めるのと同じ Rust コードが、1980 年代の Macintosh 添付ファイルを解きほぐせるのです。

まとめ

仕事に応じてエンコーダーを選びましょう:日常の文字列には base64enc、チャネルに形があるときは linewidth と newline;PEM 風の 64 文字出力が欲しい、あるいはすでにプロジェクトにあるなら openssl;速さ、ベクトル、URL 安全なエンジンが欲しいなら b64;ファイルの雑用と URL 安全な文字列には小さな専門家の base64 と base64url。base64enc::base64encode() でエンコードする前に charToRaw() で変換し、折り返しはパッケージではなくチャネルごとに選び、URL や JWT に生きるものには URL 安全なアルファベットを保ち、jose でトークンに署名し検証し、すべてのパスをラウンドトリップでテストしてください。形式そのものが容赦しないのはまさに2つの場所、アルファベットと改行だけで、エンコーダーの違いは、そのどちらかを誤ったときにどれだけ正直に教えてくれるか、そこにほぼあります。そして逆方向から呼ばれたとき - 文字列が着地し、それをばらして、バイトが何を意味するかを確認し、無言で失敗するデコーダーを生き延びなければならないとき - 姉妹記事が R での Base64 デコードを詳しく扱っています。

最終更新: 2026-10-09

関連記事: R での Base64 デコード:完全ガイド