การใช้ atob ของ Javascript เพื่อถอดรหัส base64 ไม่ได้ถอดรหัสสตริง utf-8 อย่างถูกต้อง


106

ฉันใช้window.atob()ฟังก์ชันJavascript เพื่อถอดรหัสสตริงที่เข้ารหัส base64 (โดยเฉพาะเนื้อหาที่เข้ารหัส base64 จาก GitHub API) ปัญหาคือฉันได้รับอักขระที่เข้ารหัส ASCII กลับมา (เช่นâ¢แทนที่จะเป็น) ฉันจะจัดการกับสตรีมที่เข้ารหัส base64 ขาเข้าอย่างเหมาะสมเพื่อให้ถอดรหัสเป็น utf-8 ได้อย่างไร


3
หน้า MDN ที่คุณเชื่อมโยงมีย่อหน้าที่ขึ้นต้นด้วยวลี "For use with Unicode หรือ UTF-8 strings"
Pointy

1
คุณอยู่ในโหนด? มีทางออกที่ดีกว่าatob
Bergi

คำตอบ:


269

มีบทความดีๆเกี่ยวกับเอกสาร MDN ของ Mozilla ที่อธิบายปัญหานี้อย่างตรงประเด็น:

"ปัญหา Unicode" เนื่องจากDOMStringเป็นสตริงที่เข้ารหัส 16 บิตในเบราว์เซอร์ส่วนใหญ่ที่เรียกwindow.btoaใช้สตริง Unicode จะทำให้Character Out Of Range exceptionอักขระเกินช่วงของไบต์ 8 บิต (0x00 ~ 0xFF) มีสองวิธีที่เป็นไปได้ในการแก้ปัญหานี้:

  • อันแรกคือการหลีกเลี่ยงสตริงทั้งหมด (ด้วย UTF-8 ดูencodeURIComponent) จากนั้นเข้ารหัส
  • อันที่สองคือการแปลง UTF-16 DOMStringเป็นอาร์เรย์ UTF-8 ของอักขระจากนั้นเข้ารหัส

หมายเหตุเกี่ยวกับวิธีแก้ไขปัญหาก่อนหน้านี้: บทความ MDN เดิมแนะนำให้ใช้unescapeและescapeเพื่อแก้ปัญหาCharacter Out Of Rangeข้อยกเว้น แต่หลังจากนั้นก็เลิกใช้แล้ว คำตอบอื่น ๆ บางคำแนะนำในการแก้ไขปัญหานี้decodeURIComponentและencodeURIComponentได้พิสูจน์แล้วว่าไม่น่าเชื่อถือและไม่สามารถคาดเดาได้ การอัปเดตล่าสุดสำหรับคำตอบนี้ใช้ฟังก์ชัน JavaScript ที่ทันสมัยเพื่อปรับปรุงความเร็วและปรับปรุงโค้ดให้ทันสมัย

หากคุณกำลังพยายามประหยัดเวลาให้กับตัวเองคุณสามารถพิจารณาใช้ห้องสมุด:

การเข้ารหัส 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=="

ฐานการถอดรหัส 64 ⇢ 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"

โซลูชันก่อนปี 2018 (ใช้งานได้และแม้ว่าจะรองรับเบราว์เซอร์รุ่นเก่าได้ดีกว่า แต่ยังไม่เป็นปัจจุบัน)

นี่คือคำแนะนำปัจจุบันส่งตรงจาก MDN พร้อมความเข้ากันได้กับ TypeScript เพิ่มเติมผ่านทาง @ 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"

โซลูชันดั้งเดิม (เลิกใช้แล้ว)

สิ่งนี้ใช้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"

และสิ่งสุดท้าย: ฉันพบปัญหานี้เป็นครั้งแรกเมื่อเรียก GitHub API เพื่อให้สิ่งนี้ทำงานบน Safari (มือถือ) ได้อย่างถูกต้องฉันต้องตัดพื้นที่สีขาวทั้งหมดออกจากแหล่ง base64 ก่อนที่ฉันจะสามารถถอดรหัสแหล่งที่มาได้ สิ่งนี้ยังคงเกี่ยวข้องหรือไม่ในปี 2560 ฉันไม่รู้:

function b64_to_utf8( str ) {
    str = str.replace(/\s/g, '');    
    return decodeURIComponent(escape(window.atob( str )));
}

1
w3schools.com/jsref/jsref_unescape.asp "ฟังก์ชัน unescape () เลิกใช้แล้วใน JavaScript เวอร์ชัน 1.5 ใช้ decodeURI () หรือ decodeURIComponent () แทน"
Tedd Hansen

1
คุณช่วยชีวิตฉันไว้ก่อน
Mr Neo

2
อัปเดต:โซลูชัน # 1 ใน"ปัญหา Unicode"ของ MDN ได้รับการแก้ไขb64DecodeUnicode('4pyTIMOgIGxhIG1vZGU=');แล้วขณะนี้เอาต์พุต "✓โหมด la" ถูกต้อง
weeix

2
อีกวิธีหนึ่งในการถอดรหัสจะdecodeURIComponent(atob('4pyTIMOgIGxhIG1vZGU=').split('').map(x => '%' + x.charCodeAt(0).toString(16)).join('')) ไม่ใช่รหัสที่มีประสิทธิภาพมากที่สุด แต่มันคือสิ่งที่เป็น
daniel.gindi

2
return String.fromCharCode(parseInt(p1, 16));เพื่อให้เข้ากันได้กับ TypeScript
Martin Schneider

20

สิ่งต่างๆเปลี่ยนไป หลบหนี / unescapeวิธีการได้รับการคัดค้าน

คุณสามารถ URI เข้ารหัสสตริงก่อนที่คุณจะเข้ารหัส Base64 ได้ โปรดทราบว่าสิ่งนี้ไม่ได้สร้าง UTF8 ที่เข้ารหัส Base64 แต่เป็นข้อมูลที่เข้ารหัส URL ที่เข้ารหัส 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 === '€ 你好 æøåÆØÅ'

สำหรับปัญหาของ OP ไลบรารีของบุคคลที่สามเช่นjs-base64ควรแก้ปัญหาได้


1
ฉันต้องการชี้ให้เห็นว่าคุณไม่ได้สร้าง base64 ของสตริงอินพุต แต่เป็นส่วนประกอบที่เข้ารหัสของเขา ดังนั้นหากคุณส่งไปอีกฝ่ายจะไม่สามารถถอดรหัสเป็น "base64" และรับสตริงเดิมได้
Riccardo Galli

3
คุณถูกต้องฉันได้อัปเดตข้อความเพื่อชี้ให้เห็นแล้ว ขอบคุณ. อีกทางเลือกหนึ่งคือการนำ base64 ไปใช้ด้วยตัวคุณเองโดยใช้ไลบรารีของบุคคลที่สาม (เช่น js-base64) หรือได้รับ "ข้อผิดพลาด: ไม่สามารถดำเนินการ 'btoa' ใน 'หน้าต่าง': สตริงที่จะเข้ารหัสมีอักขระที่อยู่นอกช่วง Latin1 "
Tedd Hansen

14

หากการปฏิบัติต่อสตริงเป็นไบต์เป็นสิ่งที่คุณต้องการมากกว่าคุณสามารถใช้ฟังก์ชันต่อไปนี้

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)))

1
ขอบคุณ. คำตอบของคุณสำคัญมากในการช่วยให้ฉันทำงานนี้ซึ่งใช้เวลาหลายชั่วโมงในช่วงหลายวัน +1. stackoverflow.com/a/51814273/470749
Ryan

สำหรับโซลูชันข้ามเบราว์เซอร์ที่เร็วขึ้นและมากขึ้น (แต่โดยพื้นฐานแล้วผลลัพธ์เดียวกัน) โปรดดูstackoverflow.com/a/53433503/5601591
Jack Giffin

u_atob และ u_btoa ใช้ฟังก์ชันที่มีอยู่ในทุกเบราว์เซอร์ตั้งแต่ IE10 (2012) ดูเหมือนจะมั่นคงสำหรับฉัน (ถ้าคุณอ้างถึง TextEncoder นั่นเป็นเพียงตัวอย่าง)
Riccardo Galli

5

นี่คือโซลูชันที่อัปเดตปี 2018 ตามที่อธิบายไว้ในMozilla Development Resources

การเข้ารหัสจาก UNICODE เป็น B64

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

การถอดรหัสจาก B64 เป็น UNICODE

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"

4

บทความที่สมบูรณ์ที่เหมาะกับฉัน: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding

ส่วนที่เราเข้ารหัสจาก Unicode / UTF-8 คือ

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"

นี่เป็นหนึ่งในวิธีที่ใช้กันมากที่สุดในปัจจุบัน


นั่นคือลิงค์เดียวกับคำตอบที่ยอมรับ
brandonscript

3

ฉันคิดว่าอาจต้องการโซลูชันที่สร้าง base64 URI ที่ใช้งานได้อย่างกว้างขวาง โปรดเยี่ยมชมdata:text/plain;charset=utf-8;base64,4pi44pi54pi64pi74pi84pi+4pi/เพื่อดูการสาธิต (คัดลอกข้อมูล uri เปิดแท็บใหม่วาง URI ข้อมูลลงในแถบที่อยู่จากนั้นกด Enter เพื่อไปที่หน้า) แม้ว่า URI นี้จะเข้ารหัสแบบ base64 แต่เบราว์เซอร์ก็ยังสามารถจดจำจุดรหัสที่สูงและถอดรหัสได้อย่างถูกต้อง ตัวเข้ารหัส + ตัวถอดรหัสขนาดเล็กคือ 1058 ไบต์ (+ Gzip → 589 ไบต์)

!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)

ด้านล่างนี้คือซอร์สโค้ดที่ใช้สร้าง

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

จากนั้นในการถอดรหัสข้อมูล base64 HTTP จะรับข้อมูลเป็น URI ข้อมูลหรือใช้ฟังก์ชันด้านล่าง

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

ข้อดีของการเป็นมาตรฐานมากขึ้นคือตัวเข้ารหัสนี้และตัวถอดรหัสนี้สามารถใช้งานได้อย่างกว้างขวางมากขึ้นเนื่องจากสามารถใช้เป็น URL ที่ถูกต้องซึ่งแสดงได้อย่างถูกต้อง สังเกต.

(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>

นอกจากจะได้มาตรฐานมากแล้วข้อมูลโค้ดข้างต้นยังเร็วมากอีกด้วย แทนที่จะเป็นห่วงโซ่การสืบทอดทางอ้อมที่ข้อมูลต้องถูกแปลงหลายครั้งระหว่างรูปแบบต่างๆ (เช่นในการตอบสนองของ Riccardo Galli) ข้อมูลโค้ดด้านบนจะตรงที่สุดเท่าที่จะทำได้ ใช้การString.prototype.replaceโทรด่วนเพียงครั้งเดียวในการประมวลผลข้อมูลเมื่อเข้ารหัสและมีเพียงสายเดียวในการถอดรหัสข้อมูลเมื่อถอดรหัส ข้อดีอีกประการหนึ่งคือ (โดยเฉพาะอย่างยิ่งสำหรับสตริงขนาดใหญ่) String.prototype.replaceช่วยให้เบราว์เซอร์สามารถจัดการกับการจัดการหน่วยความจำพื้นฐานของการปรับขนาดสตริงโดยอัตโนมัติซึ่งนำไปสู่การเพิ่มประสิทธิภาพที่สำคัญโดยเฉพาะในเบราว์เซอร์ที่เขียวชอุ่มตลอดปีเช่น Chrome และ Firefox ที่เพิ่มประสิทธิภาพอย่างมากString.prototype.replace. สุดท้ายไอซิ่งบนเค้กคือสำหรับผู้ใช้สคริปต์ภาษาละตินของคุณยกเว้นผู้ใช้สตริงที่ไม่มีจุดรหัสใด ๆ ที่สูงกว่า 0x7f จะประมวลผลได้เร็วเป็นพิเศษเนื่องจากสตริงยังไม่ได้รับการแก้ไขโดยอัลกอริทึมการแทนที่

ฉันได้สร้างที่เก็บ github สำหรับโซลูชันนี้ที่https://github.com/anonyco/BestBase64EncoderDecoder/


คุณสามารถอธิบายสิ่งที่คุณหมายถึงโดย "วิธีที่ผู้ใช้สร้างขึ้น" เทียบกับ "ที่เบราว์เซอร์ตีความได้" อะไรคือมูลค่าเพิ่มของการใช้โซลูชันนี้พูดสิ่งที่ Mozilla แนะนำ?
brandonscript

@brandonscript Mozilla แตกต่างจาก MDN MDN เป็นเนื้อหาที่ผู้ใช้สร้างขึ้น หน้าบน MDN ที่แนะนำโซลูชันของคุณคือเนื้อหาที่ผู้ใช้สร้างขึ้นไม่ใช่เนื้อหาที่ผู้ผลิตเบราว์เซอร์สร้างขึ้น
Jack Giffin

ผู้จำหน่ายโซลูชันของคุณสร้างขึ้นหรือไม่ ฉันขอแนะนำให้ให้เครดิตกับที่มา ถ้าไม่เช่นนั้นมันก็ถูกสร้างขึ้นโดยผู้ใช้และไม่ต่างจากคำตอบของ MDN?
brandonscript

@brandonscript จุดดี. คุณถูก. ฉันลบข้อความส่วนนั้นออก นอกจากนี้โปรดดูการสาธิตที่ฉันเพิ่ม
Jack Giffin

0

การแก้ไขขนาดเล็ก 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)));
}

2
ดูเหมือนว่าลิงก์ doc จะแตกต่างจากตอนนี้ด้วยซ้ำแนะนำโซลูชัน regex เพื่อจัดการ
brandonscript

2
สิ่งนี้จะไม่ได้ผลเพราะencodeURIComponentเป็นสิ่งที่ตรงกันข้ามdecodeURIComponentนั่นคือมันจะเลิกทำการแปลง ดูstackoverflow.com/a/31412163/1534459สำหรับคำอธิบายที่ดีของสิ่งที่เกิดขึ้นกับและescape unescape
bodo

1
@canaaerus ฉันไม่เข้าใจความคิดเห็นของคุณ? Escape และ unescape เลิกใช้งานแล้วฉันแค่สลับกับฟังก์ชัน [ถอดรหัส | เข้ารหัส] URIComponent :-) ทุกอย่างทำงานได้ดี อ่านคำถามก่อน
Darkves

1
@Darkves: เหตุผลที่encodeURIComponentใช้คือการจัดการสตริง Unicode (ช่วงทั้งหมด) อย่างถูกต้อง เช่นwindow.btoa(decodeURIComponent(encodeURIComponent('€')))ให้Error: String contains an invalid characterเพราะเหมือนกับwindow.btoa('€')และbtoaไม่สามารถเข้ารหัสได้
bodo

2
@ ดาร์คเวส: ใช่ถูกต้อง แต่คุณไม่สามารถสลับการหลบหนีด้วย EncodeURIComponent และ unescape กับ DecodeURIComponent ได้เนื่องจากการเข้ารหัสและวิธีการหลีกเลี่ยงไม่ทำสิ่งเดียวกัน เหมือนกับการถอดรหัส & unescape แต่เดิมฉันทำผิดพลาดเหมือนกัน btw คุณควรสังเกตว่าถ้าคุณใช้สตริง UriEncode จากนั้น UriDecode คุณจะได้รับสตริงเดียวกันกับที่คุณป้อนเข้าไป ดังนั้นการทำเช่นนั้นจะเป็นเรื่องไร้สาระ เมื่อคุณ unescape สตริงที่เข้ารหัสด้วย encodeURIComponent คุณจะไม่ได้รับสตริงเดียวกันกับที่คุณป้อนเข้ามานั่นคือเหตุผลว่าทำไมการใช้ Escape / unescape จึงใช้งานได้ แต่ไม่ใช่กับของคุณ
Stefan Steiger

0

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

รวมถึงวิธีแก้ปัญหาข้างต้นหากยังคงประสบปัญหาให้ลองทำตามด้านล่างนี้ให้พิจารณากรณีที่ 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(''));
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.