Декодирование base64 кодированных китайских символов в JS [дубликат]

У вас может быть библиотека JAR, скомпилированная в Java 7, и у вас есть только Java 6 как Java Runtime. Это может случиться с некоторыми новыми библиотеками.

50
задан brandonscript 7 May 2015 в 16:18
поделиться

7 ответов

В Mozilla MDN есть замечательная статья , которая описывает именно эту проблему:

«Проблема Unicode» Поскольку DOMStrings - это строки с 16-битным кодированием, в большинстве браузеры, вызывающие window.btoa в строке Unicode, вызывают исключение Character Out Of Range, если символ превышает диапазон 8-разрядного символа в кодировке ASCII. Существует два возможных способа решения этой проблемы:

  • первым из них является выход из всей строки, а затем ее кодирование,
  • , второй - преобразование UTF- 16 DOMString в массив символов UTF-8 и затем кодировать его.

Примечание к исходному ответу: ранее в статье MDN было предложено использовать unescape и escape для решения проблемы исключения Character Out Of Range, но с тех пор они устарели. Некоторые другие ответы здесь предложили обойти это с помощью decodeURIComponent и encodeURIComponent, это оказалось ненадежным и непредсказуемым.

В конце концов, вы определенно могли бы сэкономить некоторое горе с помощью библиотеки:

Вот текущая рекомендация, прямо из MDN, с некоторой дополнительной совместимостью TypeScript через @ MA-Maddin:

Кодирование UTF8 ⇢ base64

Реализовать регулярное выражение вместо устаревшей функции unescape

function b64EncodeUnicode(str) {
    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
        return String.fromCharCode(parseInt(p1, 16))
    }))
}

b64EncodeUnicode('✓ à la mode') // "4pyTIMOgIGxhIG1vZGU="
b64EncodeUnicode('\n') // "Cg=="

Декодирование base64 ⇢ UTF8

В статье MDN изначально не был пример для декодирования, но теперь добавлено

function b64DecodeUnicode(str) {
    return decodeURIComponent(Array.prototype.map.call(atob(str), function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
    }).join(''))
}

b64DecodeUnicode('4pyTIMOgIGxhIG1vZGU=') // "✓ à la mode"
b64DecodeUnicode('Cg==') // "\n"

Исходное решение (устарело)

Это использовало escape и unescape (которые теперь устарели, хотя это все еще работает во всех современных браузерах):

function utf8_to_b64( str ) {
    return window.btoa(unescape(encodeURIComponent( str )));
}

function b64_to_utf8( str ) {
    return decodeURIComponent(escape(window.atob( str )));
}

// Usage:
utf8_to_b64('✓ à la mode'); // "4pyTIMOgIGxhIG1vZGU="
b64_to_utf8('4pyTIMOgIGxhIG1vZGU='); // "✓ à la mode"

И последнее: впервые я столкнулся с этой проблемой при вызове API GitHub. Чтобы заставить это работать на (Mobile) Safari должным образом, я фактически должен был удалить все пустое пространство от источника base64 до , я мог бы даже декодировать источник. Является ли это еще актуальным в 2017 году, я не знаю:

function b64_to_utf8( str ) {
    str = str.replace(/\s/g, '');    
    return decodeURIComponent(escape(window.atob( str )));
}
125
ответ дан brandonscript 21 August 2018 в 16:45
поделиться
  • 1
    w3schools.com/jsref/jsref_unescape.asp "Функция unescape () была устарела в версии JavaScript версии 1.5. Вместо этого используйте decodeURI () или decodeURIComponent (). & Quot; – Tedd Hansen 17 February 2016 в 07:30
  • 2
    Вы спасли мои дни, брат – Mr Neo 9 April 2016 в 09:07
  • 3
    Обновление: Решение № 1 в MDN Проблема «Юникод» была исправлена, b64DecodeUnicode('4pyTIMOgIGxhIG1vZGU='); теперь корректно выводит «✓ режим la», – weeix 13 June 2016 в 06:57
  • 4
    Другой способ декодирования - decodeURIComponent(atob('4pyTIMOgIGxhIG1vZGU=').split('').map(x => '%' + x.charCodeAt(0).toString(16)).join('')) Не самый эффективный код, но это то, что есть. – daniel.gindi 5 October 2016 в 11:35
  • 5
    Ссылка base64-js мертва? – Crashalot 28 October 2016 в 04:49

Вот некоторые будущие коды для браузеров, которые могут отсутствовать escape/unescape(). Обратите внимание, что IE 9 и старше не поддерживают atob/btoa(), поэтому вам нужно будет использовать для них пользовательские функции base64.

// Polyfill for escape/unescape
if( !window.unescape ){
    window.unescape = function( s ){
        return s.replace( /%([0-9A-F]{2})/g, function( m, p ) {
            return String.fromCharCode( '0x' + p );
        } );
    };
}
if( !window.escape ){
    window.escape = function( s ){
        var chr, hex, i = 0, l = s.length, out = '';
        for( ; i < l; i ++ ){
            chr = s.charAt( i );
            if( chr.search( /[A-Za-z0-9\@\*\_\+\-\.\/]/ ) > -1 ){
                out += chr; continue; }
            hex = s.charCodeAt( i ).toString( 16 );
            out += '%' + ( hex.length % 2 != 0 ? '0' : '' ) + hex;
        }
        return out;
    };
}

// Base64 encoding of UTF-8 strings
var utf8ToB64 = function( s ){
    return btoa( unescape( encodeURIComponent( s ) ) );
};
var b64ToUtf8 = function( s ){
    return decodeURIComponent( escape( atob( s ) ) );
};

Более полным примером кодировки и декодирования UTF-8 может быть найдено здесь: http://jsfiddle.net/47zwb41o/

1
ответ дан Beejor 21 August 2018 в 16:45
поделиться

Малая коррекция, unescape и escape устарели, поэтому:

function utf8_to_b64( str ) {
    return window.btoa(decodeURIComponent(encodeURIComponent(str)));
}

function b64_to_utf8( str ) {
     return decodeURIComponent(encodeURIComponent(window.atob(str)));
}


function b64_to_utf8( str ) {
    str = str.replace(/\s/g, '');    
    return decodeURIComponent(encodeURIComponent(window.atob(str)));
}
1
ответ дан Darkves 21 August 2018 в 16:45
поделиться
  • 1
    Похоже, что ссылка doc даже отличается от этого, предлагая решение регулярного выражения для управления им. – brandonscript 9 December 2015 в 05:19
  • 2
    Это не сработает, потому что encodeURIComponent является инверсией decodeURIComponent, т. Е. Просто отменяет преобразование. См. stackoverflow.com/a/31412163/1534459 для подробного объяснения того, что происходит с escape и unescape. – bodo 1 February 2016 в 15:50
  • 3
    @canaaerus Я не понимаю ваш комментарий? escape и unescape устарели, я просто меняю их с помощью [decode | encode] URIComponent function :-) Все работает нормально. Сначала прочитайте вопрос. – Darkves 1 February 2016 в 18:21
  • 4
    @Darkves: причина, по которой используется encodeURIComponent, - правильно обрабатывать (весь диапазон) строк Unicode. Так, например, window.btoa(decodeURIComponent(encodeURIComponent('€'))) дает Error: String contains an invalid character, потому что это то же самое, что window.btoa('€') и btoa не могут кодировать . – bodo 2 February 2016 в 14:47
  • 5
    @Darkves: Да, это правильно. Но вы не можете сменить EncodeURIComponent и unescape с помощью DecodeURIComponent, потому что методы Encode и escape не делают то же самое. То же самое с декодированием и unescape. Я изначально сделал ту же ошибку, кстати. Вы должны заметить, что если вы берете строку, UriEncode, а затем UriDecode, вы получаете ту же строку, которую вы ввели. Так что это было бы бессмысленно. Когда вы освобождаете строку, закодированную с помощью encodeURIComponent, вы не получаете ту же строку, которую вы ввели, поэтому именно с помощью escape / unescape она работает, но не с вашей. – Stefan Steiger 19 July 2016 в 18:55

, включая вышеописанное решение, если все еще сталкиваются с проблемой, попробуйте, как показано ниже, рассмотрите случай, когда escape не поддерживается для TS.

blob = new Blob(["\ufeff", csv_content]); // this will make symbols to appears in excel 

для csv_content вы можете попробовать, как показано ниже.

function b64DecodeUnicode(str: any) {        
        return decodeURIComponent(atob(str).split('').map((c: any) => {
            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
        }).join(''));
    }
0
ответ дан Diwakar 21 August 2018 в 16:45
поделиться

Если обрабатывать строки как байты - это больше, вы можете использовать следующие функции

function u_atob(ascii) {
    return Uint8Array.from(atob(ascii), c => c.charCodeAt(0));
}

function u_btoa(buffer) {
    var binary = [];
    var bytes = new Uint8Array(buffer);
    for (var i = 0, il = bytes.byteLength; i < il; i++) {
        binary.push(String.fromCharCode(bytes[i]));
    }
    return btoa(binary.join(''));
}


// example, it works also with astral plane characters such as '                  
4
ответ дан Riccardo Galli 21 August 2018 в 16:45
поделиться
  • 1
    Благодарю. Ваш ответ имел решающее значение для того, чтобы помочь мне получить эту работу, и это заняло у меня много часов в течение нескольких дней. +1. stackoverflow.com/a/51814273/470749 – Ryan 13 August 2018 в 01:52

Вещи меняются. Методы escape / unescape устарели.

Вы можете кодировать строку URI перед кодировкой Base64. Обратите внимание, что это не кодирует кодировку UTF8 с кодировкой Base64, а скорее кодирует кодированные в кодировке Base64 данные. Обе стороны должны согласовать одну и ту же кодировку.

См. Рабочий пример здесь: http://codepen.io/anon/pen/PZgbPW

// encode string
var base64 = window.btoa(encodeURIComponent('€ 你好 æøåÆØÅ'));
// decode string
var str = decodeURIComponent(window.atob(tmp));
// str is now === '€ 你好 æøåÆØÅ'

Для проблемы с ОП должна решаться проблема сторонняя библиотека, такая как js-base64 .

11
ответ дан Tedd Hansen 21 August 2018 в 16:45
поделиться
  • 1
    Я хотел бы указать, что вы не создаете base64 входной строки, а его кодированный компонент. Поэтому, если вы отпустите его, другая сторона не сможет декодировать его как «base64». и получить исходную строку – Riccardo Galli 7 April 2017 в 06:10
  • 2
    Вы правы, я обновил текст, чтобы указать на это. Благодарю. Альтернативой, по-видимому, является реализация base64 самостоятельно, используя стороннюю библиотеку (например, js-base64) или получение «Ошибка: не удалось выполнить« btoa »в« Окно »: строка, подлежащая кодированию, содержит символы за пределами диапазона Latin1 . & Quot; – Tedd Hansen 4 September 2017 в 08:50
0
ответ дан Manuel G 1 November 2018 в 09:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: