Italiano

Strumenti per sviluppatori · Codificatore e decodificatore Base64

Cosa significano atob e btoa e perché capiscono solo il latino-1

· Sfondo

base64 javascript unicode

modello di unità byte di stringa binaria btoa e atob, distinto dal testo Unicode
Illustrazione vettoriale originale ToolAcre

atob e btoa risalgono a Netscape e i nomi significano 'ASCII a binario' e 'binario a ASCII'. Questo post spiega da dove provengono, come li definiscono gli standard WHATWG e perché non hanno mai imparato Unicode.

Un nome di funzione che sembra un errore di battitura: la confusione causata dai nomi e la risposta su una riga

atob e btoa sono JavaScript funzioni integrate introdotte in Netscape negli anni '90. I nomi sono abbreviazioni: btoa sta per binario a ASCII e atob sta per ASCII a binario. I nomi riflettono la loro età e design: sono stati costruiti quando binario significava una stringa di valori di byte (0-255) piuttosto che il più moderno Uint8Array o Buffer. Il mnemonico spesso fornito per i nomi è meno importante del contratto osservabile: una funzione mappa una stringa binaria su Base64 e l'altra la inverte. Questo repository non documenta la decisione di denominazione originale, quindi l'articolo evita di presentare il folklore come cronologia del browser di origine.

Le funzioni prevedono una stringa binaria: l'unità di codice di ogni carattere deve essere compresa nell'intervallo 0-255, che rappresenta un byte. Se passi un carattere con un'unità di codice sopra 255 (come un emoji o una lettera accentata esterna al latino-1), la funzione genera InvalidCharacterError o produce silenziosamente un output errato. btoa (binario in ASCII) codifica una stringa binaria in base64.

Cosa suggeriscono i nomi e cosa dimostra effettivamente il contratto della stringa di byte

L'input deve essere una stringa in cui ogni carattere è un byte (unità di codice 0-255). btoa(ciao) codifica i byte ASCII come base64 e restituisce aGVsbG8=. btoa con la lettera e-acute sembra funzionare perché la lettera precomposta latina-1 e-acute (U+00E9) ha un'unità di codice di 233, che è all'interno di 0-255. Tuttavia, btoa lo codifica come un singolo byte, 0xE9, non i UTF-8 byte 0xC3 0xA9 che e-acute dovrebbe produrre. Prima che gli array tipizzati diventassero il normale contenitore di byte, le API JavaScript utilizzavano stringhe le cui unità di codice rappresentavano byte. Quel modello rimane visibile perché btoa rifiuta le unità di codice superiori a 255. La cronologia precisa del prodotto non è stabilita da questi file; il limite di errore è stabilito da test eseguibili.

Questa corruzione silenziosa è più pericolosa di un errore: il risultato sembra buono ma è sbagliato. atob (ASCII in binario) decodifica base64 in una stringa binaria. atob(aGVsbG8=) restituisce ciao. L'output è una stringa binaria in cui l'unità di codice di ogni carattere è 0-255, che rappresenta un byte. Se vuoi convertirlo nel testo Unicode corretto, devi interpretare i byte come UTF-8 e decodificarli con TextDecoder.

Il modello legacy di stringhe binarie: comportamento osservabile senza un'affermazione non verificata sulla cronologia del browser

Per ASCII, questo passaggio aggiuntivo non è necessario (ASCII è un sottoinsieme di UTF-8), ma per qualsiasi byte nonASCII è essenziale. atob non fa questa interpretazione; restituisce i byte grezzi come una stringa binaria.

Gli standard WHATWG (gli standard di vita per le API Web) definiscono atob e btoa nella specifica HTML. La definizione include un algoritmo di decodifica base64 tollerante per atob: salta gli spazi bianchi e accetta il riempimento mancante, rendendo decodificabile il base64 del mondo reale (incluso MIME-wrapped base64 con interruzioni di riga). ToolAcre normalizza gli spazi bianchi, URL-punteggiatura sicura e riempimento mancante prima di chiamare il decodificatore del browser. Quindi copia le unità di codice restituite in Uint8Array e applica un decodificatore UTF-8 fatale. Questa combinazione separa la sintassi Base64 tollerante dalla rigorosa interpretazione del testo.

Comportamento attuale in questa implementazione: normalizzazione dell'alfabeto tollerante e rigorosa decodifica del testo UTF-8

La firma della funzione non è cambiata, ma la definizione degli standard è l'autorità per ciò che fa la funzione. Perché atob e btoa accettano solo Latin-1? Perché quando furono progettati negli anni '90, JavaScript non aveva un modo per rappresentare direttamente i byte (nessun Uint8Array o ArrayBuffer). L'unico modo per passare byte a una funzione era come una stringa in cui ogni carattere rappresenta un byte.

Questa è chiamata stringa binaria e crea confusione secondo gli standard moderni. Una stringa JavaScript è testo Unicode, non una sequenza di byte. Il progetto ha unito i due: una stringa in cui ciascuna unità di codice è 0-255 è una stringa binaria. La denominazione riflette l'epoca: ASCII in btoa significava letteralmente sette bit per il testo ASCII, ma l'implementazione accetta qualsiasi byte (0-255). L'aggiunta di una modalità Unicode direttamente a btoa cambierebbe il suo contratto di stringhe di byte di lunga data e rischierebbe la compatibilità. La fonte rivista compone invece TextEncoder prima della codifica. Questo articolo può verificare tale composizione; omette affermazioni sulle motivazioni del comitato per gli standard non registrate nel repository.

Esempio funzionante: tracciamento di forgiving-base64 su una stringa con spazi e riempimento mancante: ciò che atob accetta che un decodificatore rigoroso rifiuta

Le alternative moderne evitano il modello di stringa binaria. La codifica API fornisce TextEncoder per convertire il testo in UTF-8 byte e TextDecoder per convertire UTF-8 byte in testo.

La codifica e decodifica Base64 è ora specificata nelle specifiche HTML sia per le stringhe (atob e btoa) che per gli array tipizzati. Lo strumento di codifica e decodifica Base64 utilizza TextEncoder e TextDecoder attorno a atob e btoa, quindi puoi codificare e decodificare in modo sicuro il testo Unicode senza le limitazioni Latin-1. Un valore spaziato o senza spaziatura ha esito positivo perché la normalizzazione rimuove gli spazi bianchi e ripristina la lunghezza del blocco richiesta. Un valore la cui lunghezza pulita lascia resto uno viene rifiutato prima di atob. Questa distinzione mostra cosa significa qui "perdonante": la formattazione recuperabile è accettata, l'input strutturalmente impossibile non lo è.

Gli standard più recenti funzionano su Base64 per gli array tipizzati: descritti qualitativamente, con una nota per verificare il supporto corrente del browser

La gestione di Unicode con btoa richiede prima la codifica del testo in UTF-8 byte. La vecchia soluzione alternativa era btoa(unescape(encodeURIComponent(text))), che crea confusione ma funziona: encodeURIComponent percent-encodes UTF-8 bytes, unescape riconverte le triplette in caratteri e btoa codifica la stringa binaria risultante. Funziona ma si basa su funzioni deprecate ed è difficile da leggere. Il codice moderno dovrebbe utilizzare TextEncoder(text).map(byte => String.fromCharCode(byte)) seguito da btoa o, meglio, convertire direttamente in Uint8Array e utilizzare la codifica API.

Atob non ti fornisce automaticamente il testo; ti dà binario. atob(Y2Fmw6kg8J+YgA==) restituisce una stringa binaria contenente i byte del testo codificato UTF-8 con accento caf ed emoji. Per recuperare il testo, convertire la stringa binaria in un Uint8Array e passarla a TextDecoder(utf-8). Lo strumento di codifica e decodifica Base64 lo fa automaticamente: incolli il testo, lo codifica in UTF-8 byte, quindi in base64. Le API Base64 di array tipizzati si stanno evolvendo tra i browser, ma questa fonte non le utilizza. A seconda di uno, è necessario un controllo di compatibilità attuale e un piano di fallback. La conversione esplicita dell'array di byte di ToolAcre rimane ispezionabile e coperta dalla sua attuale suite di test.

Le alternative agli array tipizzati si stanno evolvendo: verifica il supporto attuale del browser prima di dipendere da essi

Incolli base64, viene decodificato in UTF-8 byte, quindi in testo. Il passaggio intermedio della stringa binaria è nascosto perché è un dettaglio di implementazione degli anni '90 API. Comprendere atob e btoa è utile per eseguire il debug di codice legacy o lavorare con vecchie API che forniscono stringhe binarie. La maggior parte del nuovo codice dovrebbe evitare completamente il modello di stringa binaria.

Se è necessario codificare o decodificare Base64, lo strumento di codifica e decodifica Base64 gestisce correttamente Unicode. Se stai creando un API, accetta Uint8Array o una vista di array digitato oppure documenta chiaramente se il tuo base64 è UTF-8 o Latin-1. Quando si esamina il codice che utilizza btoa con testo nonASCII senza TextEncoder, si verifica un bug: l'output codifica i byte errati. Il buffer del nodo e i runtime non browser definiscono API e regole di accettazione diverse. Sono intenzionalmente esclusi. Le affermazioni contenute in questo articolo riguardano le primitive del browser e il wrapper implementato in apps/dev, non tutte le funzioni denominate atob o btoa in ogni ambiente.

Conclusione: due funzioni degli anni '90 con un contratto di stringa di byte: come il codificatore e decodificatore Base64 fa il passo UTF-8 attorno a loro in modo che accenti, CJK ed emoji andata e ritorno

I nomi atob e btoa sono peculiari artefatti dell'informatica degli anni '90. La denominazione moderna sarebbe base64Encode e base64Decode e le API accetterebbero Uint8Array o stringhe con dichiarazioni di codifica esplicite. Ma atob e btoa persistono nei browser per compatibilità con le versioni precedenti. Comprendere cosa significano (e cosa non possono fare) ti aiuta a evitare la corruzione silenziosa durante la codifica del testo Unicode.

Lo strumento di codifica e decodifica Base64 colma il divario: parla i linguaggi UTF-8 e base64 di cui ha bisogno il codice moderno. Il modello robusto è compositivo: codifica il testo in UTF-8 byte, converte i byte nel contratto di stringa binaria, quindi chiama btoa; invertire questi passaggi attorno ad atob. Prova un accento, caratteri CJK ed emoji, quindi richiedi che il testo decodificato corrisponda a ogni punto di codice originale.