Die Verwendung von atav von Javascript zum Dekodieren von base64 dekodiert utf-8-Zeichenfolgen nicht richtig

106

Ich verwende die Javascript- window.atob()Funktion, um eine Base64-codierte Zeichenfolge zu dekodieren (insbesondere den Base64-codierten Inhalt der GitHub-API). Das Problem ist, dass ich ASCII-codierte Zeichen zurück bekomme (wie â¢anstelle von ). Wie kann ich den eingehenden Base64-codierten Stream richtig verarbeiten, damit er als utf-8 dekodiert wird?

Brandonscript
quelle
3
Die von Ihnen verknüpfte MDN-Seite enthält einen Absatz, der mit dem Satz "Zur Verwendung mit Unicode- oder UTF-8-Zeichenfolgen" beginnt.
Pointy
1
Bist du auf Knoten? Es gibt bessere Lösungen alsatob
Bergi

Antworten:

268

Es gibt einen großartigen Artikel in Mozillas MDN-Dokumenten, der genau dieses Problem beschreibt:

Das "Unicode-Problem" Da es sich bei DOMStrings um 16-Bit-codierte Zeichenfolgen handelt, führt der Aufruf window.btoaeiner Unicode-Zeichenfolge in den meisten Browsern dazu, Character Out Of Range exceptiondass ein Zeichen den Bereich eines 8-Bit-Bytes (0x00 ~ 0xFF) überschreitet. Es gibt zwei mögliche Methoden, um dieses Problem zu lösen:

  • Die erste besteht darin, den gesamten String zu maskieren (siehe UTF-8 encodeURIComponent) und ihn dann zu codieren.
  • Die zweite besteht darin, das UTF-16 DOMStringin ein UTF-8-Array von Zeichen zu konvertieren und es dann zu codieren.

Ein Hinweis zu früheren Lösungen: Der MDN-Artikel schlug ursprünglich vor , das Ausnahmeproblem zu verwenden unescapeund escapezu lösen Character Out Of Range, aber seitdem sind sie veraltet. Einige andere Antworten hier haben vorgeschlagen , um diese Arbeit mit decodeURIComponentund encodeURIComponenthat diese unzuverlässig und unberechenbar erwiesen. Das neueste Update dieser Antwort verwendet moderne JavaScript-Funktionen, um die Geschwindigkeit zu verbessern und den Code zu modernisieren.

Wenn Sie versuchen, sich Zeit zu sparen, können Sie auch eine Bibliothek verwenden:

Codierung von UTF8 ⇢ base64

function b64EncodeUnicode(str) {
    // first we use encodeURIComponent to get percent-encoded UTF-8,
    // then we convert the percent encodings into raw bytes which
    // can be fed into btoa.
    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
        function toSolidBytes(match, p1) {
            return String.fromCharCode('0x' + p1);
    }));
}

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

Decodieren von base64 ⇢ UTF8

function b64DecodeUnicode(str) {
    // Going backwards: from bytestream, to percent-encoding, to original string.
    return decodeURIComponent(atob(str).split('').map(function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
}

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

Die Lösung vor 2018 (funktionale und wahrscheinlich bessere Unterstützung für ältere Browser, nicht auf dem neuesten Stand)

Hier ist die aktuelle Empfehlung direkt von MDN mit einigen zusätzlichen TypeScript-Kompatibilitäten über @ MA-Maddin:

// Encoding UTF8 ⇢ base64

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=="

// Decoding base64 ⇢ UTF8

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"

Die ursprüngliche Lösung (veraltet)

Dies verwendet escapeund unescape(die jetzt veraltet sind, obwohl dies immer noch in allen modernen Browsern funktioniert):

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"

Und noch eine letzte Sache: Ich bin zum ersten Mal auf dieses Problem gestoßen, als ich die GitHub-API aufgerufen habe. Damit dies auf (Mobile) Safari ordnungsgemäß funktioniert, musste ich tatsächlich den gesamten Leerraum von der base64-Quelle entfernen, bevor ich die Quelle überhaupt dekodieren konnte. Ob dies 2017 noch relevant ist oder nicht, weiß ich nicht:

function b64_to_utf8( str ) {
    str = str.replace(/\s/g, '');    
    return decodeURIComponent(escape(window.atob( str )));
}
Brandonscript
quelle
1
w3schools.com/jsref/jsref_unescape.asp "Die Funktion unescape () wurde in JavaScript Version 1.5 nicht mehr unterstützt. Verwenden Sie stattdessen decodeURI () oder decodeURIComponent ()."
Tedd Hansen
1
Sie haben meine Tage gerettet, Bruder
Herr Neo
2
Update: Lösung Nr. 1 in MDNs Das "Unicode-Problem" wurde behoben, b64DecodeUnicode('4pyTIMOgIGxhIG1vZGU=');jetzt wird korrekt "✓ à la mode"
ausgegeben
2
Eine andere Art zu dekodieren wäre decodeURIComponent(atob('4pyTIMOgIGxhIG1vZGU=').split('').map(x => '%' + x.charCodeAt(0).toString(16)).join('')) nicht der leistungsstärkste Code, aber es ist, was es ist.
daniel.gindi
2
return String.fromCharCode(parseInt(p1, 16));TypeScript-Kompatibilität haben.
Martin Schneider
20

Dinge ändern sich. Die Escape- / Unescape- Methoden sind veraltet.

Sie können die Zeichenfolge mit einem URI codieren, bevor Sie sie mit Base64 codieren. Beachten Sie, dass hierdurch kein Base64-codiertes UTF8 erzeugt wird, sondern Base64-codierte URL-codierte Daten. Beide Seiten müssen sich auf die gleiche Kodierung einigen.

Siehe Arbeitsbeispiel hier: http://codepen.io/anon/pen/PZgbPW

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

Für das Problem von OP sollte eine Bibliothek eines Drittanbieters wie js-base64 das Problem lösen.

Tedd Hansen
quelle
1
Ich möchte darauf hinweisen, dass Sie nicht die base64 der Eingabezeichenfolge, sondern seiner codierten Komponente erzeugen. Wenn Sie es also wegschicken, kann die andere Partei es nicht als "base64" dekodieren und die Originalzeichenfolge erhalten
Riccardo Galli
3
Sie haben Recht, ich habe den Text aktualisiert, um darauf hinzuweisen. Vielen Dank. Die Alternative scheint darin zu bestehen, base64 selbst zu implementieren, eine Bibliothek eines Drittanbieters (z. B. js-base64) zu verwenden oder "Fehler: Fehler beim Ausführen von 'btoa' in 'Window' zu erhalten: Die zu codierende Zeichenfolge enthält Zeichen außerhalb des Bereichs Latin1. ""
Tedd Hansen
14

Wenn Sie Strings eher als Bytes behandeln möchten, können Sie die folgenden Funktionen verwenden

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 '𝒞'
var encodedString = new TextEncoder().encode('✓');
var base64String = u_btoa(encodedString);
console.log('✓' === new TextDecoder().decode(u_atob(base64String)))
Riccardo Galli
quelle
1
Vielen Dank. Ihre Antwort war entscheidend, um mir zu helfen, dies zum Laufen zu bringen, was mich über mehrere Tage viele Stunden gekostet hat. +1. stackoverflow.com/a/51814273/470749
Ryan
Eine viel schnellere und browserübergreifendere Lösung (aber im Wesentlichen dieselbe Ausgabe) finden Sie unter stackoverflow.com/a/53433503/5601591
Jack Giffin,
u_atob und u_btoa verwenden Funktionen, die seit IE10 (2012) in jedem Browser verfügbar sind. Sie sehen für mich solide aus (wenn Sie sich auf TextEncoder beziehen, ist dies nur ein Beispiel)
Riccardo Galli,
4

Hier ist die 2018 aktualisierte Lösung, wie in den Mozilla-Entwicklungsressourcen beschrieben

VON UNICODE NACH B64 ENCODIEREN

function b64EncodeUnicode(str) {
    // first we use encodeURIComponent to get percent-encoded UTF-8,
    // then we convert the percent encodings into raw bytes which
    // can be fed into btoa.
    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
        function toSolidBytes(match, p1) {
            return String.fromCharCode('0x' + p1);
    }));
}

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

VON B64 ZU UNICODE DEKODIEREN

function b64DecodeUnicode(str) {
    // Going backwards: from bytestream, to percent-encoding, to original string.
    return decodeURIComponent(atob(str).split('').map(function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
}

b64DecodeUnicode('4pyTIMOgIGxhIG1vZGU='); // "✓ à la mode"
b64DecodeUnicode('Cg=='); // "\n"
Manuel G.
quelle
3

Ich würde annehmen, dass man eine Lösung möchte, die einen weit verbreiteten base64-URI erzeugt. Bitte besuchen Sie data:text/plain;charset=utf-8;base64,4pi44pi54pi64pi74pi84pi+4pi/, um eine Demonstration zu sehen (kopieren Sie die Daten-URL, öffnen Sie eine neue Registerkarte, fügen Sie die Daten-URI in die Adressleiste ein und drücken Sie die Eingabetaste, um zur Seite zu gelangen). Trotz der Tatsache, dass dieser URI base64-codiert ist, kann der Browser die hohen Codepunkte weiterhin erkennen und ordnungsgemäß decodieren. Der minimierte Encoder + Decoder ist 1058 Bytes (+ Gzip → 589 Bytes)

!function(e){"use strict";function h(b){var a=b.charCodeAt(0);if(55296<=a&&56319>=a)if(b=b.charCodeAt(1),b===b&&56320<=b&&57343>=b){if(a=1024*(a-55296)+b-56320+65536,65535<a)return d(240|a>>>18,128|a>>>12&63,128|a>>>6&63,128|a&63)}else return d(239,191,189);return 127>=a?inputString:2047>=a?d(192|a>>>6,128|a&63):d(224|a>>>12,128|a>>>6&63,128|a&63)}function k(b){var a=b.charCodeAt(0)<<24,f=l(~a),c=0,e=b.length,g="";if(5>f&&e>=f){a=a<<f>>>24+f;for(c=1;c<f;++c)a=a<<6|b.charCodeAt(c)&63;65535>=a?g+=d(a):1114111>=a?(a-=65536,g+=d((a>>10)+55296,(a&1023)+56320)):c=0}for(;c<e;++c)g+="\ufffd";return g}var m=Math.log,n=Math.LN2,l=Math.clz32||function(b){return 31-m(b>>>0)/n|0},d=String.fromCharCode,p=atob,q=btoa;e.btoaUTF8=function(b,a){return q((a?"\u00ef\u00bb\u00bf":"")+b.replace(/[\x80-\uD7ff\uDC00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g,h))};e.atobUTF8=function(b,a){a||"\u00ef\u00bb\u00bf"!==b.substring(0,3)||(b=b.substring(3));return p(b).replace(/[\xc0-\xff][\x80-\xbf]*/g,k)}}(""+void 0==typeof global?""+void 0==typeof self?this:self:global)

Unten finden Sie den Quellcode, mit dem er generiert wurde.

var fromCharCode = String.fromCharCode;
var btoaUTF8 = (function(btoa, replacer){"use strict";
    return function(inputString, BOMit){
        return btoa((BOMit ? "\xEF\xBB\xBF" : "") + inputString.replace(
            /[\x80-\uD7ff\uDC00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g, replacer
        ));
    }
})(btoa, function(nonAsciiChars){"use strict";
    // make the UTF string into a binary UTF-8 encoded string
    var point = nonAsciiChars.charCodeAt(0);
    if (point >= 0xD800 && point <= 0xDBFF) {
        var nextcode = nonAsciiChars.charCodeAt(1);
        if (nextcode !== nextcode) // NaN because string is 1 code point long
            return fromCharCode(0xef/*11101111*/, 0xbf/*10111111*/, 0xbd/*10111101*/);
        // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
        if (nextcode >= 0xDC00 && nextcode <= 0xDFFF) {
            point = (point - 0xD800) * 0x400 + nextcode - 0xDC00 + 0x10000;
            if (point > 0xffff)
                return fromCharCode(
                    (0x1e/*0b11110*/<<3) | (point>>>18),
                    (0x2/*0b10*/<<6) | ((point>>>12)&0x3f/*0b00111111*/),
                    (0x2/*0b10*/<<6) | ((point>>>6)&0x3f/*0b00111111*/),
                    (0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/)
                );
        } else return fromCharCode(0xef, 0xbf, 0xbd);
    }
    if (point <= 0x007f) return nonAsciiChars;
    else if (point <= 0x07ff) {
        return fromCharCode((0x6<<5)|(point>>>6), (0x2<<6)|(point&0x3f));
    } else return fromCharCode(
        (0xe/*0b1110*/<<4) | (point>>>12),
        (0x2/*0b10*/<<6) | ((point>>>6)&0x3f/*0b00111111*/),
        (0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/)
    );
});

Um die base64-Daten zu dekodieren, ruft HTTP die Daten entweder als Daten-URI ab oder verwendet die folgende Funktion.

var clz32 = Math.clz32 || (function(log, LN2){"use strict";
    return function(x) {return 31 - log(x >>> 0) / LN2 | 0};
})(Math.log, Math.LN2);
var fromCharCode = String.fromCharCode;
var atobUTF8 = (function(atob, replacer){"use strict";
    return function(inputString, keepBOM){
        inputString = atob(inputString);
        if (!keepBOM && inputString.substring(0,3) === "\xEF\xBB\xBF")
            inputString = inputString.substring(3); // eradicate UTF-8 BOM
        // 0xc0 => 0b11000000; 0xff => 0b11111111; 0xc0-0xff => 0b11xxxxxx
        // 0x80 => 0b10000000; 0xbf => 0b10111111; 0x80-0xbf => 0b10xxxxxx
        return inputString.replace(/[\xc0-\xff][\x80-\xbf]*/g, replacer);
    }
})(atob, function(encoded){"use strict";
    var codePoint = encoded.charCodeAt(0) << 24;
    var leadingOnes = clz32(~codePoint);
    var endPos = 0, stringLen = encoded.length;
    var result = "";
    if (leadingOnes < 5 && stringLen >= leadingOnes) {
        codePoint = (codePoint<<leadingOnes)>>>(24+leadingOnes);
        for (endPos = 1; endPos < leadingOnes; ++endPos)
            codePoint = (codePoint<<6) | (encoded.charCodeAt(endPos)&0x3f/*0b00111111*/);
        if (codePoint <= 0xFFFF) { // BMP code point
          result += fromCharCode(codePoint);
        } else if (codePoint <= 0x10FFFF) {
          // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
          codePoint -= 0x10000;
          result += fromCharCode(
            (codePoint >> 10) + 0xD800,  // highSurrogate
            (codePoint & 0x3ff) + 0xDC00 // lowSurrogate
          );
        } else endPos = 0; // to fill it in with INVALIDs
    }
    for (; endPos < stringLen; ++endPos) result += "\ufffd"; // replacement character
    return result;
});

Der Vorteil von mehr Standard ist, dass dieser Encoder und dieser Decoder breiter anwendbar sind, da sie als gültige URL verwendet werden können, die korrekt angezeigt wird. Beobachten.

(function(window){
    "use strict";
    var sourceEle = document.getElementById("source");
    var urlBarEle = document.getElementById("urlBar");
    var mainFrameEle = document.getElementById("mainframe");
    var gotoButton = document.getElementById("gotoButton");
    var parseInt = window.parseInt;
    var fromCodePoint = String.fromCodePoint;
    var parse = JSON.parse;
    
    function unescape(str){
        return str.replace(/\\u[\da-f]{0,4}|\\x[\da-f]{0,2}|\\u{[^}]*}|\\[bfnrtv"'\\]|\\0[0-7]{1,3}|\\\d{1,3}/g, function(match){
          try{
            if (match.startsWith("\\u{"))
              return fromCodePoint(parseInt(match.slice(2,-1),16));
            if (match.startsWith("\\u") || match.startsWith("\\x"))
              return fromCodePoint(parseInt(match.substring(2),16));
            if (match.startsWith("\\0") && match.length > 2)
              return fromCodePoint(parseInt(match.substring(2),8));
            if (/^\\\d/.test(match)) return fromCodePoint(+match.slice(1));
          }catch(e){return "\ufffd".repeat(match.length)}
          return parse('"' + match + '"');
        });
    }
    
    function whenChange(){
      try{ urlBarEle.value = "data:text/plain;charset=UTF-8;base64," + btoaUTF8(unescape(sourceEle.value), true);
      } finally{ gotoURL(); }
    }
    sourceEle.addEventListener("change",whenChange,{passive:1});
    sourceEle.addEventListener("input",whenChange,{passive:1});
    
    // IFrame Setup:
    function gotoURL(){mainFrameEle.src = urlBarEle.value}
    gotoButton.addEventListener("click", gotoURL, {passive: 1});
    function urlChanged(){urlBarEle.value = mainFrameEle.src}
    mainFrameEle.addEventListener("load", urlChanged, {passive: 1});
    urlBarEle.addEventListener("keypress", function(evt){
      if (evt.key === "enter") evt.preventDefault(), urlChanged();
    }, {passive: 1});
    
        
    var fromCharCode = String.fromCharCode;
    var btoaUTF8 = (function(btoa, replacer){
		    "use strict";
        return function(inputString, BOMit){
        	return btoa((BOMit?"\xEF\xBB\xBF":"") + inputString.replace(
        		/[\x80-\uD7ff\uDC00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g, replacer
    		));
    	}
    })(btoa, function(nonAsciiChars){
		"use strict";
    	// make the UTF string into a binary UTF-8 encoded string
    	var point = nonAsciiChars.charCodeAt(0);
    	if (point >= 0xD800 && point <= 0xDBFF) {
    		var nextcode = nonAsciiChars.charCodeAt(1);
    		if (nextcode !== nextcode) { // NaN because string is 1code point long
    			return fromCharCode(0xef/*11101111*/, 0xbf/*10111111*/, 0xbd/*10111101*/);
    		}
    		// https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
    		if (nextcode >= 0xDC00 && nextcode <= 0xDFFF) {
    			point = (point - 0xD800) * 0x400 + nextcode - 0xDC00 + 0x10000;
    			if (point > 0xffff) {
    				return fromCharCode(
    					(0x1e/*0b11110*/<<3) | (point>>>18),
    					(0x2/*0b10*/<<6) | ((point>>>12)&0x3f/*0b00111111*/),
    					(0x2/*0b10*/<<6) | ((point>>>6)&0x3f/*0b00111111*/),
    					(0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/)
    				);
    			}
    		} else {
    			return fromCharCode(0xef, 0xbf, 0xbd);
    		}
    	}
    	if (point <= 0x007f) { return inputString; }
    	else if (point <= 0x07ff) {
    		return fromCharCode((0x6<<5)|(point>>>6), (0x2<<6)|(point&0x3f/*00111111*/));
    	} else {
    		return fromCharCode(
    			(0xe/*0b1110*/<<4) | (point>>>12),
    			(0x2/*0b10*/<<6) | ((point>>>6)&0x3f/*0b00111111*/),
    			(0x2/*0b10*/<<6) | (point&0x3f/*0b00111111*/)
    		);
    	}
    });
    setTimeout(whenChange, 0);
})(window);
img:active{opacity:0.8}
<center>
<textarea id="source" style="width:66.7vw">Hello \u1234 W\186\0256ld!
Enter text into the top box. Then the URL will update automatically.
</textarea><br />
<div style="width:66.7vw;display:inline-block;height:calc(25vw + 1em + 6px);border:2px solid;text-align:left;line-height:1em">
<input id="urlBar" style="width:calc(100% - 1em - 13px)" /><img id="gotoButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAeCAMAAADqx5XUAAAAclBMVEX///9NczZ8e32ko6fDxsU/fBoSQgdFtwA5pAHVxt+7vLzq5ex23y4SXABLiiTm0+/c2N6DhoQ6WSxSyweVlZVvdG/Uz9aF5kYlbwElkwAggACxs7Jl3hX07/cQbQCar5SU9lRntEWGum+C9zIDHwCGnH5IvZAOAAABmUlEQVQoz7WS25acIBBFkRLkIgKKtOCttbv//xdDmTGZzHv2S63ltuBQQP4rdRiRUP8UK4wh6nVddQwj/NtDQTvac8577zTQb72zj65/876qqt7wykU6/1U6vFEgjE1mt/5LRqrpu7oVsn0sjZejMfxR3W/yLikqAFcUx93YxLmZGOtElmEu6Ufd9xV3ZDTGcEvGLbMk0mHHlUSvS5svCwS+hVL8loQQyfpI1Ay8RF/xlNxcsTchGjGDIuBG3Ik7TMyNxn8m0TSnBAK6Z8UZfp3IbAonmJvmsEACum6aNv7B0CnvpezDcNhw9XWsuAr7qnRg6dABmeM4dTgn/DZdXWs3LMspZ1KDMt1kcPJ6S1icWNp2qaEmjq6myx7jbQK3VKItLJaW5FR+cuYlRhYNKzGa9vF4vM5roLW3OSVjkmiGJrPhUq301/16pVKZRGFYWjTP50spTxBN5Z4EKnSonruk+n4tUokv1aJSEl/MLZU90S3L6/U6o0J142iQVp3HcZxKSo8LfkNRCtJaKYFSRX7iaoAAUDty8wvWYR6HJEepdwAAAABJRU5ErkJggg==" style="width:calc(1em + 4px);line-height:1em;vertical-align:-40%;cursor:pointer" />
<iframe id="mainframe" style="width:66.7vw;height:25vw" frameBorder="0"></iframe>
</div>
</center>

Die oben genannten Codefragmente sind nicht nur sehr standardisiert, sondern auch sehr schnell. Anstelle einer indirekten Folgekette, bei der die Daten mehrmals zwischen verschiedenen Formen konvertiert werden müssen (wie in Riccardo Gallis Antwort), ist das obige Codefragment so direkt wie möglich. Es wird nur ein einfaches Fasten verwendet . Das i-Tüpfelchen ist schließlich, dass Zeichenfolgen, die keine Codepunkte über 0x7f enthalten, für Benutzer mit lateinischem Skript-Exclūsīvō besonders schnell verarbeitet werden können, da die Zeichenfolge durch den Ersetzungsalgorithmus unverändert bleibt.String.prototype.replace Aufruf zum Verarbeiten der Daten beim Codieren und nur ein Aufruf zum Decodieren der Daten beim Decodieren verwendet. Ein weiteres Plus ist, dass String.prototype.replaceder Browser (insbesondere bei großen Zeichenfolgen) die zugrunde liegende Speicherverwaltung für die Größenänderung der Zeichenfolge automatisch übernimmt, was insbesondere bei immergrünen Browsern wie Chrome und Firefox, die stark optimiert sind, zu einer erheblichen Leistungssteigerung führtString.prototype.replace

Ich habe ein Github-Repository für diese Lösung unter https://github.com/anonyco/BestBase64EncoderDecoder/ erstellt.

Jack Giffin
quelle
Können Sie näher erläutern, was Sie unter "vom Benutzer erstellter Weg" und "vom Browser interpretierbar" verstehen? Welchen Mehrwert bietet die Verwendung dieser Lösung beispielsweise gegenüber den Empfehlungen von Mozilla?
Brandonscript
@brandonscript Mozilla unterscheidet sich von MDN. MDN ist vom Benutzer erstellter Inhalt. Die Seite auf MDN, auf der Ihre Lösung empfohlen wird, war vom Benutzer erstellter Inhalt, nicht vom Browser vom Hersteller erstellter Inhalt.
Jack Giffin
Ist Ihr Lösungsanbieter erstellt? Ich würde vorschlagen, den Ursprung zu würdigen. Wenn nicht, wird es auch vom Benutzer erstellt und unterscheidet sich nicht von der Antwort von MDN?
Brandonscript
@brandonscript Guter Punkt. Du hast Recht. Ich habe diesen Text entfernt. Schauen Sie sich auch die Demo an, die ich hinzugefügt habe.
Jack Giffin
2

Der vollständige Artikel, der für mich funktioniert: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding

Der Teil, in dem wir von Unicode / UTF-8 codieren, ist

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"

Dies ist heutzutage eine der am häufigsten verwendeten Methoden.

Rika
quelle
Das ist der gleiche Link wie die akzeptierte Antwort.
Brandonscript
0

Kleine Korrekturen, Flucht und Flucht sind veraltet, also:

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)));
}
Darkves
quelle
2
Es sieht so aus, als ob sich der Doc-Link jetzt sogar von diesem unterscheidet und eine Regex-Lösung für die Verwaltung vorschlägt.
Brandonscript
2
Dies funktioniert nicht, da encodeURIComponentes umgekehrt ist decodeURIComponent, dh es wird nur die Konvertierung rückgängig gemacht. Unter stackoverflow.com/a/31412163/1534459 finden Sie eine ausführliche Erklärung, was mit escapeund passiert unescape.
Bodo
1
@canaaerus Ich verstehe deinen Kommentar nicht? Escape und Unescape sind veraltet, ich tausche sie einfach mit der URIComponent-Funktion [decode | encode] aus :-) Alles funktioniert einwandfrei. Lesen Sie die Frage zuerst
Darkves
1
@Darkves: Der Grund, warum encodeURIComponentverwendet wird, ist die korrekte Behandlung (des gesamten Bereichs von) Unicode-Zeichenfolgen. Also zB window.btoa(decodeURIComponent(encodeURIComponent('€')))gibt es, Error: String contains an invalid characterweil es das gleiche ist wie window.btoa('€')und btoanicht codieren kann .
Bodo
2
@ Darkves: Ja, das ist richtig. Sie können Escape jedoch nicht mit EncodeURIComponent austauschen und mit DecodeURIComponent entfernen, da die Encode- und die Escape-Methode nicht dasselbe tun. Gleiches gilt für Decodieren & Entweichen. Ich habe ursprünglich den gleichen Fehler gemacht, übrigens. Sie sollten beachten, dass Sie, wenn Sie eine Zeichenfolge, UriEncode und dann UriDecode, verwenden, dieselbe Zeichenfolge zurückerhalten, die Sie eingegeben haben. Das wäre also Unsinn. Wenn Sie eine mit encodeURIComponent codierte Zeichenfolge entfernen, erhalten Sie nicht dieselbe Zeichenfolge zurück, die Sie eingegeben haben. Deshalb funktioniert dies mit Escape / Unescape, jedoch nicht mit Ihrer.
Stefan Steiger
0

Hier ist ein zukunftssicherer Code für Browser, die möglicherweise fehlen escape/unescape(). Beachten Sie, dass IE 9 und älter dies nicht unterstützen atob/btoa(). Sie müssen daher benutzerdefinierte base64-Funktionen für sie verwenden.

// 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 ) ) );
};

Ein umfassenderes Beispiel für die UTF-8-Codierung und -Decodierung finden Sie hier: http://jsfiddle.net/47zwb41o/

Beejor
quelle
-1

Wenn Sie immer noch auf ein Problem stoßen, versuchen Sie es wie folgt. Betrachten Sie den Fall, in dem Escape für TS nicht unterstützt wird.

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

Für csv_content können Sie es wie folgt versuchen.

function b64DecodeUnicode(str: any) {        
        return decodeURIComponent(atob(str).split('').map((c: any) => {
            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
        }).join(''));
    }
Diwakar
quelle