Você tem que lidar com o formato Base64? Então esse site é perfeito para você! Use o nosso ferramenta online super útil para codificar ou decodificar os seus dados.

Codificação Base64 em R: um guia completo

Você tem bytes que precisam viajar, e a estrada só aceita texto. Um JPEG que precisa morar dentro de um campo JSON. Um certificado que precisa ficar numa variável de ambiente. Um gráfico que precisa viajar dentro de um relatório HTML autocontido. O Base64 é a fita adesiva para tudo isso: qualquer sequência de bytes vira uma string de 64 caracteres inofensivos que sobrevive a qualquer canal de texto que você possa jogar nele. A página inicial deste site cobre o formato por inteiro, então aqui vai a versão curta: três bytes entram, quatro caracteres saem, tirados de A a Z, a a z, 0 a 9, mais + e /, com um pouco de padding = no final quando a carga não divide certo.

O detalhe específico do R: o R base não traz nenhum codificador Base64. Não existe um base64_encode() esperando em algum pacote base, e não existe builtin de uma linha para você recorrer. Você escolhe um pacote, e o ecossistema de fato te dá escolha, com velocidades diferentes, hábitos de quebra de linha diferentes e opiniões diferentes sobre padding. Até o fim deste artigo você vai saber qual codificador usar em cada situação, e qual deles vai silenciosamente fazer outra coisa que não codificar.

O cenário dos codificadores

Cinco pacotes fazem a codificação, e eles se dividem nos cavalos de batalha do dia a dia, nos próximos da criptografia e nos pequenos especialistas. Aqui está o elenco, atualizado até 2026:

Pacote Versão (2026) Pontos de entrada de codificação Hábitos de quebra de linha Quando recorrer a ele
base64enc 0.1-6 base64encode() linewidth e newline, inteiramente para você definir strings do dia a dia, quebra MIME
openssl 2.4.2 base64_encode() linhas de 64 caracteres, quebras LF, newline final arquivos PEM, pilhas de criptografia existentes
b64 0.1.7 encode(), encode_file() nunca quebra; b64_chunk() e b64_wrap() sob demanda velocidade, vetores, motores seguros para URL
base64 2.0.2 encode() linhas de 64 caracteres mais um newline final por padrão tarefas de arquivo para arquivo, imagens de relatório
base64url 1.4 base64_urlencode() nunca quebra, sem padding, entrada em caractere strings seguras para URL

Três codificadores a mais se escondem dentro de pacotes que você talvez já carregue. O jsonlite exporta base64_enc() e base64url_enc(), então se você já faz parse de JSON, pode já ter um codificador à mão. O jose exporta base64url_encode() para trabalho com JWT. E o veterano pacote RCurl ainda carrega um wrapper base64() em volta do libcurl que funciona bem e pertence a uma era anterior. O pacote base64, por fim, agora se descreve na lata como wrapper de compatibilidade e aponta aplicações novas para base64enc, openssl ou jsonlite.

Preparando o terreno

Se o R ainda não está na máquina, o seu sistema operacional o fornece: r-base no Debian e no Ubuntu, R no Fedora, Homebrew ou o instalador oficial no macOS, um instalador no Windows. Depois os codificadores, direto do CRAN:

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

Os dois lugares onde as instalações saem dos trilhos são ambos em tempo de compilação. O openssl compila contra o OpenSSL do seu sistema, então uma caixa Linux pura quer primeiro os cabeçalhos de desenvolvimento (sudo apt install libssl-dev), ou você pode pular a compilação inteira no Debian e no Ubuntu com sudo apt install r-cran-openssl. E o b64 é um motor em Rust embrulhado com extendr, então uma compilação do source quer a toolchain do Rust (sudo apt install cargo também puxa o rustc). Windows e macOS recebem binários pré-compilados do CRAN e nada disso se aplica.

A primeira codificação

Noventa por cento da vida de codificação cabe em três linhas, usando a mesma famosa string que o lado da decodificação usa como seu teste de fumaça:

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

Três coisas para notar naquela cerimônia. Primeira, a entrada é um vetor raw: charToRaw() é a ponte da sua string R para os bytes que vão ser empacotados, e os codificadores do dia a dia desta tabela - base64enc, openssl, b64 - todos aceitam raw. Segunda, a saída é a direção oposta da dos decodificadores: uma única string de caracteres, porque a codificação termina no lado do texto da fronteira. Terceira, veja a matemática do comprimento em ação: três bytes entram, quatro caracteres saem, sem necessidade de padding porque a carga divide certo. Quando a carga não divide, um ou dois caracteres = caem no final.

E como um codificador que você não confia é pior que nenhum, aqui está o round trip que prova que as duas direções concordam:

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

Strings, bytes e a armadilha do nome de arquivo

Agora a armadilha, porque todo desenvolvedor R cai nela uma vez. base64encode() trata um argumento character como um nome de arquivo, não como texto para codificar. Passe uma string e ele sai procurando aquele arquivo:

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"

O aviso é a pista: ele tentou file("Man", "rb"), ou seja, "abrir um arquivo chamado Man para leitura raw". Então com o base64encode(), a disciplina é um único reflexo: charToRaw() primeiro, sempre. Se um arquivo é de fato o que você quer dizer, é isso que a função está fazendo, e a saída são os bytes do arquivo empacotados, o que às vezes é exatamente o que você quer.

O b64 toma uma posição diferente na mesma questão: o seu encode() aceita um vetor de caracteres diretamente, trata cada elemento como texto UTF-8, e é vetorizado para completar:

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

Duas bordas a mais desta fronteira valem a pena conhecer. A entrada vazia é codificada de três formas diferentes, dependendo de quem você perguntar:

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

O base64enc responde com um vetor de caracteres de comprimento zero, não uma string vazia, então código a jusante que espera uma string e recebe character(0) falha em lugares surpreendentes. E a decisão do charset mora também no lado da codificação: charToRaw() empacota a string na codificação que ela está vestindo no momento, então texto UTF-8 viaja como bytes UTF-8, que é o que a outra ponta do fio espera. Emoji incluídos, já que o R moderno armazena code points acima de U+FFFF como UTF-8 verdadeiro:

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

Quebra de linha: MIME, PEM e a sua própria largura

Strings Base64 longas são quebradas em linhas, porque os canais de texto mais antigos do mundo tinham limites de coluna, e o MIME nunca se deu ao trabalho de esquecê-los. As duas quebras históricas que você vai encontrar são o MIME, que quebra em 76 caracteres com CRLF entre as linhas, e o PEM, que quebra em 64. Cada codificador tem a sua própria ideia de qual deles, se algum, usar, então esta é a seção onde você escolhe o contrato antes de codificar.

A matemática do tamanho primeiro, porque quebra de linha é sobre saber o quanto a saída fica longa. Quatro caracteres saem para cada três bytes, o que torna o comprimento da forma codificada um teto simples:

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

Com isso no bolso, os codificadores. O base64enc é o mais flexível: por padrão ele emite uma linha sem quebras, e o argumento linewidth te entrega um vetor de linhas em vez disso:

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

Duas linhas para 100 bytes com largura 76, um vetor por padrão, uma única string juntada com CRLF quando você adiciona newline. Não há elemento vazio no final: 114 bytes, que codificam para exatamente duas linhas de 76, voltam como duas linhas, não três.

O openssl toma o outro polo. linebreaks = TRUE quebra em 64 caracteres com quebras LF simples, e adiciona mais um newline no final absoluto:

wrapped <- openssl::base64_encode(charToRaw(strrep("a", 100)), linebreaks = TRUE)
nchar(wrapped)
#> [1] 139          # 136 caracteres de dados mais 3 quebras de linha
nchar(strsplit(wrapped, "\n", fixed = TRUE)[[1]])
#> [1] 64 64 8

Conte a aritmética: 136 caracteres de dados, duas quebras internas, uma quebra final, 139 no total. E aquela quebra final é invisível para a contagem de linhas mais natural que você pode escrever, porque o strsplit() descarta um pedaço vazio no final, então o vetor diz três linhas enquanto a string carrega quatro. Se um dia você comparar uma saída OpenSSL quebrada com uma saída MIME quebrada e as contagens de caracteres não baterem, este é o fantasma.

O b64 não quebra de forma alguma; ele te dá as duas operações separadamente, e a largura do chunk tem uma regra, ser múltiplo de quatro, porque o motor se recusa a cortar um grupo Base64 ao meio:

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.

E o pacote orientado a arquivos base64 segue o exemplo do OpenSSL: linhas de 64 caracteres, um newline final, ligado por padrão:

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

Aquele zero final é o newline final, pego pelo readLines() como uma linha final vazia. Aqui está o campo inteiro de uma vez:

Codificador Largura Quebra de linha Newline final
base64encode(x) uma linha nenhuma nenhum
base64encode(x, linewidth = 76, newline = "\r\n") 76 CRLF nenhum
openssl::base64_encode(x, linebreaks = TRUE) 64 LF sim
b64::encode(x) uma linha nenhuma (quebre com b64_chunk() e b64_wrap()) nenhum
base64::encode(in, out) 64 LF sim
base64url::base64_urlencode(x) uma linha nenhuma nenhum

Base64 seguro para URL

O Base64 padrão gasta as duas últimas vagas do seu alfabeto no + e no /, e são exatamente esses caracteres que as URLs não gostam: o mais vira %2B, a barra vira %2F, e cada = de padding vira %3D. A variante segura para URL, definida na seção 5 do RFC 4648, troca aquelas duas letras por - e _ e normalmente descarta o padding, então um token que deveria ser colável em qualquer lugar continua colável em qualquer lugar. O R tem três portas para lá.

Os motores do b64 são os mais completos: um motor é um alfabeto configurado e uma política de padding, e o pacote traz os quatro que você precisa. Alimente os mesmos três bytes para o motor standard e para o motor seguro para URL e veja o alfabeto fazer o seu trabalho:

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"

Quatro motores, "standard", "standard_no_pad", "url_safe" e "url_safe_no_pad", e o mesmo objeto de motor funciona nas duas direções, o que mantém o seu código simétrico. As variantes sem padding só diferem quando o padding de fato apareceria, como no exemplo de um byte acima.

O pacote dedicado base64url é a porta de propósito único: caractere entra, string segura para URL sai, nunca quebra, nunca dá padding:

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

E o jose exporta o seu próprio base64url_encode() para trabalho com JWT, devolvendo vetores raw no lado da decodificação do jeito que você esperaria. Uma regra amarra as três portas: o alfabeto com o qual você codifica é o alfabeto com o qual você deve decodificar. Entregue a string "----" a um decodificador standard e ele falha no primeiro traço; o artigo irmão de decodificação cobre como cada decodificador encontra entrada suja ou descasada.

JWTs: fazendo o token

O JSON Web Token é onde o Base64 seguro para URL virou uso diário. Um JWT é três partes base64url juntadas por pontos: um header descrevendo como ele foi assinado, um payload de claims JSON, e uma assinatura que amarra os dois. No lado da codificação você está construindo os três, e o pacote jose faz a cerimônia inteira. A sua API é construída em torno das funções jwt_* (jwt_claim(), jwt_encode_hmac(), jwt_decode_hmac(), jwt_split()); a release 2.0 (abril de 2026) adiciona suporte a ED25519 e torna o campo de header typ opcional:

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"

Note o que o jwt_claim() fez nos bastidores: iat (issued at) tem como padrão o tempo atual, então ele apareceu no payload sem ser pedido, enquanto exp tem como padrão nada, o que significa um token sem expiração, o que normalmente não é o que você quer. Defina exp de propósito, e a assinatura cuida do resto. jwt_split() é a ferramenta de inspeção: o header, o payload como uma lista nomeada, e a assinatura raw, sem verificação envolvida, exatamente o passo de espiar que o lado da decodificação deste par de artigos descreve.

O lado da verificação é onde o jose ganha o seu pão. jwt_decode_hmac() checa a assinatura e aplica os claims de tempo, e o seu estilo de recusa é específico:

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

No sucesso você recebe os claims de volta como uma lista comum, então round$sub e companhia simplesmente funcionam. Um claim nbf (not before) no futuro ganha a sua própria recusa, Token is not valid before ..., e um token HMAC não será decodificado pelo assimétrico jwt_decode_sig(), que responde Unsupported algorithm: HMAC e quer uma chave pública em vez disso. Duas notas finais. Primeira, o payload é codificado, não criptografado: qualquer um pode ler cada claim, então nada de segredo pertence a um. Segunda, se você carregar o httr2 depois do jose, fique de olho na mensagem de mascaramento: o httr2 exporta o seu próprio jwt_claim(), jwt_encode_hmac() e jwt_encode_sig(), construídos para credenciais de cliente OAuth com exp padrão para cinco minutos adiante, e eles ofuscam as versões do jose pelo resto da sessão.

Arquivos: binário entra, texto sai

Codificar um arquivo é o espelho do trabalho de arquivos do lado da decodificação, e o pacote b64 tem o ponto de entrada mais claro, que também é o mais rápido porque nunca constrói uma string intermediária enorme:

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"

O aviso é um recurso disfarçado: cat() não adiciona um newline final, então o arquivo termina no meio de uma linha, e o readLines() te avisa. Lembre de que lado desse fato você está quando o arquivo será consumido pelo b64::decode_file(), que paniqueia num newline final; o artigo de decodificação tem a história completa. Escreva com cat() ou writeBin(charToRaw(enc), path) e a borda fica obtusa.

O pacote base64 é a opção puramente de arquivo para arquivo, com o par de funções correspondentes e quebra no estilo OpenSSL como padrão:

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

Ele devolve o caminho de saída, o que é conveniente para log. E quando você quer ver a mecânica, o pipeline manual funciona com qualquer codificador da tabela: leia o arquivo como raw, codifique, escreva o texto, pronto:

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 e documentos autocontidos

O esquema data: de URI (RFC 2397) é o consumidor mais visível do lado da codificação: um documento que carrega o seu próprio conteúdo, um tipo MIME, a palavra "base64" e o payload, tudo num único atributo. Os bytes mágicos do PNG tornam o padrão reconhecível: todo PNG Base64 no mundo real começa com os mesmos caracteres, porque o header do arquivo 89 50 4E 47 sempre codifica do mesmo jeito:

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

Se você já grepou um arquivo HTML por iVBOR para encontrar imagens embutidas, é por isso que a impressão digital funciona. Relatórios R Markdown, dashboards de arquivo único e páginas raspadas usam todos a mesma forma, e construir um segue o mesmo padrão: leia o arquivo como raw, codifique, e cole atrás do prefixo MIME. O custo é a matemática do tamanho de antes, um terço maior que o original, morando no seu HTML para sempre, então mantenha as imagens embutidas magras.

APIs e requisições web

O lado da decodificação deste par de artigos encontra APIs que te entregam Base64; este lado encontra APIs que querem Base64. O padrão é o mesmo toda vez: codifique os bytes, coloque a string no corpo JSON, envie. O cliente HTTP moderno é o 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

Três notas para o caminho. O construtor de request é o request(), e ele tem esse nome desde a primeira release do httr2. O corpo da resposta, quando você lê respostas, chega como um vetor raw, então rawToChar() antes do parse. E a API pode falar um dialeto: algumas querem o alfabeto seguro para URL, algumas querem o padding removido, e uma API JSON não tem problema com = num valor de string, então a questão do padding só vira questão de URL quando o Base64 viaja no path ou na query. Na dúvida, leia os exemplos da API em vez da especificação na sua cabeça.

Bancos de dados, configuração e ambiente

Base64 num banco de dados é binário contrabandeado através de uma coluna de texto, e o lado da codificação é empacotar um blob antes de ele entrar. Aqui está o round trip contra SQLite através do DBI e do 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"

A alternativa é armazenar os bytes nativamente como BLOB, no qual caso nenhum Base64 é necessário e a coluna volta para o R como um vetor raw. A variante Base64-em-TEXT existe para portabilidade: você pode inspecioná-la com um editor de texto, fazer diff, e toda outra linguagem pode lê-la sem drivers binários. O mesmo argumento a leva para a configuração, onde um certificado ou um segredo é armazenado como string em YAML, JSON ou numa variável de ambiente:

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

Uma cautela pertence aqui, porque arquivos de configuração são onde os segredos moram: Base64 é codificação, não criptografia. Um valor Base64 num arquivo de configuração é legível por qualquer um que possa ler o arquivo. Ele sobrevive ao transporte e a editores de texto; ele não protege nada.

E-mail

O e-mail é onde a quebra de 76 caracteres nasceu, e anexos MIME ainda a vestem: conteúdo Base64, quebrado em 76 caracteres com CRLF entre as linhas, dentro de uma parte que declara Content-Transfer-Encoding: base64. O codificador que produz exatamente aquela forma é o base64encode() com os seus argumentos de quebra definidos para o contrato MIME:

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"

O R não tem cliente de correio de primeira classe, mas o ponto se mantém sempre que você constrói ou inspeciona partes MIME na mão, gera fixtures .eml, ou faz parse de anexos a partir de um: esta é a forma que o Base64 precisa ter, e o lado da decodificação deste par de artigos mostra os decodificadores lenientes desembrulhando a caminho de volta.

Payloads grandes e o teto de strings

Strings do R têm um teto rígido de 2^31 - 1 bytes, e como a forma codificada é cerca de um terço maior que o original, um arquivo de aproximadamente 1,5 GB de dados crus empurraria a sua codificação de uma linha para além da parede. A jogada prática é a mesma que o base64enc oferece desde a sua release de vetores longos de 2022: mantenha a saída como linhas, não uma string:

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

Dez megabytes de zeros viram 183961 linhas de no máximo 76 caracteres, um vetor perfeitamente comum que você pode escrever linha por linha ou passar em stream por um pipe sem nunca segurar uma string enorme. Para o caso de data frame, uma coluna de muitos valores binários, o b64 é o campeão de velocidade: o seu motor em Rust codifica a coluna inteira numa chamada vetorizada, o que é uma diferença dramática de fazer loop linha por linha. Se você tem uma coluna de valores codificados para produzir, rode uma comparação rápida com system.time() você mesmo; a diferença entre um loop por linha e uma chamada vetorizada costuma ser grande o suficiente para fazer diferença.

A linha de comando

Nem tudo precisa de uma sessão R completa. A ferramenta Unix clássica fala Base64 nativamente, codificando sem flags em todas as plataformas, e decodificando com -d no Linux e -D no macOS e nos BSDs:

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

Fique de olho no -n da primeira linha: sem ele, o echo contribui com um newline, e a saída codifica 14 bytes em vez de 13, terminando em o= em vez de IQ==. Um base64 GNU também quebra por você (-w 76), o que é útil ao pipear para algo que espera entrada formatada como MIME. E um Rscript de uma linha faz o mesmo trabalho com os mesmos pacotes que você usa nos seus scripts:

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

Use o shell para verificações rápidas e pipes; use R quando o resultado precise morar num data frame, num arquivo ou num relatório. E não cole strings de vários megabytes no terminal: argumentos de linha de comando esbarram no ARG_MAX muito antes do que o Base64, então pipe por um arquivo em vez disso.

Armadilhas que valem a pena conhecer

Aqui está a lista curta das formas como o lado da codificação morde desenvolvedores R, todas nativas do ecossistema e não do Base64 em geral:

  • Uma string é um nome de arquivo. base64encode("Man") tenta abrir um arquivo chamado Man. O aviso nomeia o arquivo; o erro diz que a conexão falhou. Use charToRaw() primeiro com o base64encode().
  • Entrada vazia é codificada de três formas. base64encode(raw(0)) devolve character(0), enquanto b64::encode("") e base64url::base64_urlencode("") devolvem "". Código de string a jusante não espera um vetor de comprimento zero.
  • openssl quebra e depois adiciona uma linha fantasma. linebreaks = TRUE quebra em 64 com LF e acrescenta um newline final que o strsplit() descarta em silêncio, então contagens de linhas e de caracteres ingênuas divergem por uma linha.
  • Quebra de linha é um contrato. MIME é 76 com CRLF, PEM é 64, APIs JSON normalmente não querem nada. Escolha a forma que a outra ponta espera, porque um decodificador que tolera um estilo de quebra rejeita outro.
  • O alfabeto seguro para URL deve bater nas duas pontas. "----" codificado seguro para URL falha num decodificador standard no primeiro traço, e o padding vira %3D no momento em que a string mora numa URL.
  • b64_chunk exige múltiplos de quatro. Qualquer outra largura ganha Chunk size must be a multiple of 4., porque um grupo Base64 não pode ser cortado ao meio.
  • Um newline final pode paniquear o decodificador. Arquivos que você escreve para o b64::decode_file() ler devem terminar sem newline; cat(), não writeLines().
  • Claims de tempo de JWT são aplicados. jwt_decode_hmac() recusa tokens expirados e claims nbf futuros, e o httr2 ofusca as funções jwt_* do jose se você carregá-lo em segundo.
  • O payload é visível. Claims Base64 num JWT, num data URI ou num arquivo de configuração são legíveis por qualquer um. Codificação não é criptografia.
  • O teto de string é uma parede, não uma diretriz. Cerca de 1,5 GB de dados crus por string R é onde uma codificação de uma linha para de caber; mantenha a saída grande como linhas ou passe em stream.

Boas práticas

  • Converta com charToRaw() antes de codificar ao usar base64enc::base64encode(). Se você precisa de entrada de caractere direta e vetorização, b64::encode() é o pacote que trata strings como strings.
  • Padrão para o trabalho do dia a dia: base64enc::base64encode(), recorra ao b64 quando você quer velocidade, vetorização verdadeira ou os motores seguros para URL, e use openssl quando ele já está no projeto e você quer saída formatada como PEM.
  • Escolha a quebra por canal, não por pacote: nada para JSON e URLs, 76 com CRLF para MIME, 64 para blocos estilo PEM, e mantenha consistência para o lado decodificador do pipe saber o que esperar.
  • Use o alfabeto seguro para URL sem padding para qualquer coisa que vai morar numa URL, num JWT ou num nome de arquivo, e o alfabeto standard para e-mail.
  • Para JWTs, defina exp (e iat) explicitamente, verifique com jwt_decode_hmac() antes de confiar num token, e lembre que cada claim é texto público.
  • Teste os seus caminhos de codificação com um round trip, identical(text, rawToChar(base64decode(base64encode(charToRaw(text))))); é uma linha e ele pega erros de alfabeto, padding e charset de uma vez.
  • Para arquivos, prefira b64::encode_file() ou base64::encode() a ler o arquivo inteiro numa string, e escreva a sua saída com cat() quando um decodificador estrito vai lê-la.
  • Mantenha a fita adesiva honesta: o Base64 faz bytes viajarem, ele não os torna secretos e não os torna menores. Criptografe primeiro se o segredo é o objetivo, comprima primeiro se o tamanho é.

Como o Base64 chegou ao R

O formato chegou muito antes de o R fazer alguma coisa com ele. Foi padronizado para o protocolo Privacy-Enhanced Mail em 1987 (RFC 989), adotado pelo MIME em 1993 (RFC 1521, depois o final RFC 2045 em 1996, que ainda define a quebra de 76 caracteres), arrumado no RFC 3548 em 2003, e recebeu a sua forma moderna no RFC 4648 em 2006, que adicionou o alfabeto seguro para URL, a opção sem padding e o seu irmão menor Base32. A história do R começou em setembro de 2012, quando o base64enc de Simon Urbanek chegou ao CRAN e silenciosamente virou a resposta padrão para "como eu faço Base64 nisso" por mais de uma década, ganhando checkUTF8() em 2015 e suporte a vetores longos em 2022. O mundo da criptografia entrou pelo openssl, o wrapper de longa duração de Jeroen Ooms em volta do OpenSSL do sistema, cujo base64_encode() é a opção formatada como PEM desde então. O antigo pacote base64, também de Ooms, foi reeditado em outubro de 2024 explicitamente como wrapper de compatibilidade, a sua própria descrição agora apontando aplicações novas para outro lugar. Depois o b64 chegou em janeiro de 2024, um motor em Rust construído com extendr que trouxe vetorização e um estábulo de alfabetos, e em abril de 2026 o jose soltou a versão 2.0, adicionando suporte a ED25519 à API jwt_*, tornando tokens assinados um cidadão de primeira classe. O resultado é uma caixa de ferramentas com um codificador por trabalho: strings do dia a dia, blocos PEM, velocidade, URLs, arquivos e tokens.

Cantinho dos fatos divertidos

Porque um guia completo deveria terminar com um sorriso:

  • Todo PNG Base64 na internet começa com os mesmos caracteres: o header mágico 89 50 4E 47 codifica para iVBOR, então grep um arquivo HTML por essa impressão digital encontra toda imagem embutida. Formatos têm impressões digitais, e esta é um prefixo.
  • base64encode("Man") não codifica a palavra Man. Ele sai procurando um arquivo chamado Man, avisa que não consegue abri-lo, e desiste. A armadilha mais específica do R em todo o ecossistema Base64, escondida a olho nu na lista de argumentos.
  • Uma string quebrada pelo OpenSSL sempre termina com um newline final, então a última linha de um bloco PEM nunca é a última linha do arquivo. A quebra tem um ponto no final da frase, queira você ou não.
  • A string vazia é codificada de três formas diferentes: base64enc devolve um vetor de zero strings, b64 e base64url devolvem a string vazia. O R encontra nada, e o R dá três respostas.
  • O jose escreve o header do JWT com typ antes de alg, enquanto a maioria dos exemplos escritos à mão coloca alg primeiro. JSON não se importa com a ordem das chaves, e a verificação de JWT sabe disso, mas o seu diff de string não.
  • O limite de 76 caracteres do MIME é uma decisão de 1993 sobre o comprimento de linha de mensagem, carregada por quatro RFCs até todo anexo de e-mail que você já enviou. A quebra que você está definindo hoje foi discutida antes do R ter um gráfico colorido.
  • b64 vai decodificar alfabetos que você nunca viu, BinHex, UTF-7 modificado do IMAP, bcrypt, crypt e o par seguro para URL, com um motor cada. O mesmo código Rust que empacota o seu campo JSON pode desempacotar um anexo de Macintosh dos anos 1980.

Resumo final

Escolha o seu codificador pelo trabalho: base64enc para strings do dia a dia, com linewidth e newline quando o canal tem uma forma; openssl quando você quer saída de 64 caracteres no estilo PEM ou ele já está no projeto; b64 quando você quer velocidade, vetores ou os motores seguros para URL; e os pequenos especialistas base64 e base64url para tarefas de arquivo e strings seguras para URL. Converta com charToRaw() antes de codificar com base64enc::base64encode(), escolha a quebra por canal em vez de por pacote, mantenha o alfabeto seguro para URL para qualquer coisa que vai morar numa URL ou num JWT, assine e verifique tokens com jose, e teste cada caminho com um round trip. O formato em si é implacável em exatamente dois lugares, o alfabeto e as quebras de linha, e os codificadores diferem principalmente em quão honestamente te contam quando você errou um deles. E quando a outra direção chamar - quando uma string chega e você precisa desmontá-la, checar o que os bytes significam, e sobreviver aos decodificadores que falham em silêncio - o artigo irmão cobre a decodificação Base64 no R em detalhes.

Última atualização: 2026-09-08

Artigo relacionado: Decodificação Base64 em R: um guia completo