Kiedy Base64 wydaje się „łamać język chiński”, problemem jest zwykle granica między znakami i bajtami. Base64 nie wie, że sekwencja reprezentuje chiński tekst lub emoji. Zachowuje bajty; aplikacja wybiera sposób tworzenia i interpretacji tych bajtów.
Postępuj zgodnie z bajtami
Znak 中 to U+4E2D. W UTF-8 używa bajtów E4 B8 AD, których reprezentacja Base64 to 5Lit. Różne kodowanie znaków generuje różne bajty, a zatem inną wartość Base64. Uzgodnij UTF-8 na obu końcach przed porównaniem zakodowanych ciągów.
const bytes = new TextEncoder().encode('中');
console.log([...bytes]); // [228, 184, 173]
console.log([...bytes].map(b => b.toString(16)).join(' ')); // e4 b8 ad
Emoji ilustrują kolejne rozróżnienie: JavaScript '😊'.length to dwie jednostki kodu UTF-16, podczas gdy jego reprezentacja UTF-8 ma cztery bajty. Żadna liczba sama w sobie nie jest liczbą widocznych znaków postrzeganych przez użytkownika w dowolnym tekście. Użyj długości bajtów do obliczeń plików i transportu.
Spraw, aby wymiana była widoczna
Znak zastępczy � może oznaczać, że wcześniejszy dekoder utracił już informacje. Po zastąpieniu nieznanych bajtów w ciągu i ponownym zakodowaniu tego ciągu, oryginalnych wartości zwykle nie można zrekonstruować. Przed wykonaniem dalszych konwersji sprawdź nieprzetworzoną zdekodowaną tablicę bajtów.
const decoder = new TextDecoder('utf-8', { fatal: true });
console.log(decoder.decode(Uint8Array.of(0xe4, 0xb8, 0xad))); // 中
try { decoder.decode(Uint8Array.of(0xff)); }
catch { console.log('Invalid UTF-8; preserve the original byte.'); }
Normalizacja jest osobną operacją
Dwa ciągi mogą wyglądać identycznie, jeśli zawierają różne sekwencje Unicode. Akcentowana litera é może być jednym wstępnie utworzonym znakiem lub literą e, po której następuje akcent łączący. UTF-8 i Base64 poprawnie zachowują tę różnicę. Jeśli Twoja aplikacja wymaga znormalizowanego tekstu, wybierz jawnie formę normalizacji i zastosuj ją przed kodowaniem; nigdy nie nazywaj zmienionego wyniku naprawą zachowującą bajty.
Jeśli wyniki się różnią, sprawdź także niewidoczne znaki, zakończenia linii i możliwe znaczniki kolejności bajtów. Są to prawdziwe bajty wejściowe, niekoniecznie błędy kodera. W przypadku podpisanych wiadomości i sum kontrolnych nawet niewidoczna różnica zmienia wynik.
Niezawodna kolejność rozwiązywania problemów jest następująca: zachowaj źródło, sprawdź bajty, zidentyfikuj oczekiwane kodowanie znaków, dekoduj z jawną obsługą błędów, a dopiero potem rozważ normalizację. MDN: btoa() wyjaśnia, dlaczego bezpośrednie wywołania btoa przeglądarki nie nadają się do dowolnego tekstu Unicode. Pliki binarne powinny całkowicie omijać dekodowanie tekstu.