เครื่องมือสำหรับนักพัฒนา · ตัวเข้ารหัสและตัวถอดรหัส Base64
การถอดรหัส Base64 ถึง UTF-8 โดยไม่ต้องใช้ mojibake: atob บวก TextDecoder
· มันทำงานอย่างไร
base64 การเข้ารหัส ยูนิโค้ด
atob() ส่งคืนไบต์ที่ปลอมตัวเป็นอักขระ ซึ่งเป็นเหตุผลว่าทำไมข้อความที่มีการเน้นเสียงจึงดูใช้งานไม่ได้หลังจากการถอดรหัส โพสต์นี้แสดงไปป์ไลน์ที่ถูกต้องจาก Base64 เป็นไบต์ถึงข้อความ UTF-8 และวิธีการจดจำรูปแบบความล้มเหลว
การตอบสนอง API ที่ถอดรหัสเป็น 'Café' — อาการโมจิเบคที่เป็นรูปธรรมและไบต์สองไบต์ที่อยู่ด้านหลังอักขระผิดสองตัว
สตริง Base64 Q2Fmw6k= ถอดรหัสเป็นไบต์ (67, 97, 102, 195, 169) ซึ่งเป็น UTF-8 text Café วางลงในตัวถอดรหัสไร้เดียงสา (เพียงแปลง atob และสตริง) และเอาต์พุตมักจะเป็น Café แต่ละสำเนียงจะถูกแทนที่ด้วยอักขระผิดสองตัว โมจิเบคนี้เกิดขึ้นเนื่องจาก atob ส่งคืนสตริงไบต์ (หน่วยโค้ด 0–255) ไม่ใช่ข้อความ UTF-8 ไบต์ 195 และ 169 เข้ารหัสแบบเน้นเสียง é ใน UTF-8
การปฏิบัติต่อพวกเขาเสมือนว่าอักขระ Latin-1 แยกกันจะให้รูปแบบ mojibake ไปป์ไลน์ที่ถูกต้องคือ atob (ไบต์เป็นสตริง) จากนั้น TextDecoder (ตีความไบต์เป็น UTF-8) และข้อความต้นฉบับกลับมา ฟังก์ชัน atob ไม่เสียหาย มันถูกออกแบบมาสำหรับข้อมูลไบนารี ชื่อของมันมาจาก ASCII-to-binary และสตริงไบนารี่ที่สร้างขึ้นคือลำดับของหน่วยโค้ด 0–255 แต่ละหน่วยแทนหนึ่งไบต์
สิ่งที่ atob() ส่งคืนจริง ๆ — สตริงของหน่วยโค้ด 0–255 ที่ย่อมาจากไบต์ ไม่ใช่ข้อความที่ถอดรหัส
หากคุณป้อน Q2Fmw6k= (Base64 มาตรฐาน) มันจะส่งออกสตริงโดยที่อักขระแต่ละตัวมีหนึ่งไบต์: รหัสหน่วย 67 จากนั้น 97 จากนั้น 102 จากนั้น 195 จากนั้น 169 หากแสดงสตริงนั้นโดยตรงหรือแปลเป็นข้อความ Latin-1 คุณจะเห็นเอาต์พุตที่อ่านไม่ออก ขั้นตอนที่ขาดหายไปคือการแปลงหน่วยโค้ดเป็นอาร์เรย์ไบต์แล้วถอดรหัสอาร์เรย์เป็น UTF-8
charCodeAt loop กู้คืนค่าไบต์: สำหรับแต่ละอักขระในเอาต์พุต atob ให้เรียก charCodeAt เพื่อรับหน่วยโค้ด (หมายเลข 0–255) เก็บไว้ใน Uint8Array เมื่อมีอาร์เรย์ของไบต์แล้ว ให้ส่งต่อไปยัง TextDecoder ด้วยชุดอักขระ utf-8 TextDecoder อ่านลำดับไบต์และแปลเป็นข้อความ UTF-8 รวมลำดับไบต์เช่น (195, 169) ให้เป็นอักขระเดี่ยวเช่น é ไบต์ (67, 97, 102, 195, 169) กลายเป็น Café สตริงสี่อักขระ การวนซ้ำนั้นน่าเบื่อโดยเจตนา: อ่านแต่ละหน่วยโค้ดที่ส่งคืนด้วย charCodeAt และกำหนดให้กับตำแหน่ง Uint8Array ที่ตรงกัน ไม่มีการตัดสินใจเกี่ยวกับการกำหนดตัวละครเกิดขึ้นที่นั่น การตีความเพียงอย่างเดียวจะเกิดขึ้นเมื่อ TextDecoder ได้รับอาร์เรย์นั้นและใช้ UTF-8 กับการจัดการข้อผิดพลาดร้ายแรง
เปลี่ยนสตริงนั้นเป็น Uint8Array - charCodeAt วนซ้ำและเหตุใดจึงเป็นสำเนาไบต์ไม่ใช่การแปลง
กระบวนการสองขั้นตอนนี้—การกู้คืนไบต์ จากนั้นการตีความ UTF-8—คือสิ่งที่ตัวเข้ารหัสและตัวถอดรหัส Base64 ทำภายใน รูปแบบโมจิเบเกะเป็นสัญญาณบ่งบอกถึงข้อผิดพลาดนี้ หาก Café ปรากฏเป็น Café แสดงว่าคุณกำลังเห็นการตีความแบบละติน-1 ของ UTF-8 ไบต์ UTF-8 ไบต์สำหรับ é คือ 0xC3 0xA9 (ทศนิยม 195, 169) ในภาษาละติน-1 หน่วยโค้ด 195 คือ à และหน่วยโค้ด 169 คือ ©
เมื่อลำดับไบต์ UTF-8 ถูกอ่านเหมือนกับว่าแต่ละไบต์มีอักขระ Latin-1 แยกกัน ทุกลำดับ UTF-8 แบบหลายไบต์จะสร้างอักขระทดแทนที่ไม่ถูกต้อง หาก Café ปรากฏเป็น Caf ตามด้วยตัวละครทดแทน หรือเป็น Caf? หรือ Caf บวก U+FFFD คุณเห็นความล้มเหลวที่แตกต่างกัน: ตัวถอดรหัสไม่รู้จักลำดับไบต์ว่าถูกต้อง UTF-8 ตัวอย่างที่เป็นรูปธรรม: Base64 SGVsbG8sIOS4lueVjCEg8J-Zgg== ถอดรหัสดังนี้
TextDecoder และการตัดสินใจชุดอักขระ - ถอดรหัสเป็น UTF-8 และเหตุใดชุดอักขระจึงเป็นข้อเท็จจริงแยกต่างหากที่คุณต้องรู้
atob สร้างสตริงไบนารี่ด้วยไบต์ (72, 101, 108, 108, 111, 44, 32, 228, 184, 180, 149, 140, 33, 32, 240, 159, 152, 130) หกไบต์แรกคือ ASCII: กลายเป็น Hello,. ไบต์ 228, 184, 180 เป็นลำดับ UTF-8 สามไบต์ที่แสดงถึงอักขระ CJK ไบต์ 149, 140 เป็นส่วนหนึ่งของลำดับถัดไป ลำดับที่สมบูรณ์ประกอบด้วยลำดับอิโมจิสี่ไบต์ (240, 159, 152, 130) สำหรับอักขระตัวสุดท้าย
เมื่อประมวลผลอย่างถูกต้องผ่าน TextDecoder ไบต์ทั้งหมดจะรวมกันเพื่อสร้างข้อความสคริปต์ผสมต้นฉบับ ลำดับไบต์ UTF-8 มีความยาวที่คาดเดาได้: ไบต์ที่เริ่มต้นด้วย 0xxxxxxx คือไบต์เดี่ยว ASCII; ไบต์ที่เริ่มต้นด้วย 110xxxxx คาดว่าไบต์ต่อไปนี้จะเริ่มต้นด้วย 10xxxxxx (รวมสองไบต์) ไบต์ที่เริ่มต้นด้วย 1110xxxx คาดว่าจะมีไบต์ต่อไปนี้สองไบต์ (รวมเป็นสามไบต์) ไบต์ที่ขึ้นต้นด้วย 11110xxx คาดว่าจะมีไบต์ต่อไปนี้สามไบต์ (รวมเป็นสี่ไบต์) สำหรับตัวอย่าง CJK และอิโมจิแบบผสม มุมมองไบต์จะได้รับการวินิจฉัยเป็นพิเศษ เนื่องจากสัญชาตญาณ ASCII ไม่ช่วยอีกต่อไป แต่ละสัญลักษณ์ที่มองเห็นได้จะมีไบต์หลายไบต์ และการลบหนึ่งไบต์จะเลื่อนลำดับที่เหลือไปเป็น UTF-8 ที่ไม่ถูกต้อง การถอดรหัสที่เข้มงวดจะเปลี่ยนไปสู่ความล้มเหลวที่ระบุชื่อ แทนที่จะเป็นความเสียหายที่ดูเป็นไปได้
ตัวอย่างการทำงาน: การถอดรหัสสตริง Base64 ที่มี CJK และอิโมจิ — ไบต์, รหัสพอยต์ และสตริงสุดท้ายเมื่อเปรียบเทียบกับต้นฉบับ
ลำดับที่ขึ้นต้นด้วย 1111110x ไม่ถูกต้องใน UTF-8 (สงวนไว้สำหรับอนาคต ไม่ได้ใช้) ไบต์ที่ขึ้นต้นด้วย 10xxxxxx ไม่ควรปรากฏเป็นไบต์นำ มันเป็นความต่อเนื่อง หากสตรีมไบต์ละเมิดกฎ UTF-8 ไม่ถูกต้อง TextDecoder พร้อมชุดอักขระ utf-8 ตีความอาร์เรย์ตามกฎเหล่านี้และดำเนินการสำเร็จสำหรับลำดับที่ถูกต้อง หากไม่ถูกต้อง ระบบจะรายงานข้อผิดพลาด
ตัวเข้ารหัสและตัวถอดรหัส Base64 ใช้ TextDecoder พร้อมการตั้งค่าสถานะโหมดเข้มงวดจริง ซึ่งหมายความว่า UTF-8 ที่ไม่ถูกต้องทำให้เกิดข้อผิดพลาดแทนที่จะแทรกอักขระแทนที่โดยไม่ตั้งใจ (U+FFFD) หากสตริง Base64 ถอดรหัสเป็นไบต์ที่ไม่ถูกต้อง UTF-8 โหมดเข้มงวดจะหยุดทำงานแทนที่จะดำเนินการต่อด้วยข้อความที่อ่านไม่ออก นี่คือตัวเลือกการออกแบบ: เพย์โหลดไบนารี (รูปภาพ คีย์ ข้อมูลที่ถูกบีบอัด) ไม่ใช่ข้อความและไม่ควรถอดรหัสเป็นข้อความ
การรับรู้รูปแบบ: Ã, †และ � — วิธีบอกปัญหา Base64 จากปัญหาชุดอักขระ
หากพยายามถอดรหัส JPEG เป็น Base64 สตรีมไบต์จะไม่แสดงถึง UTF-8 ที่ถูกต้อง และการถอดรหัสแบบเข้มงวดจะปฏิเสธ เครื่องมือเสนอมุมมองเลขฐานสิบหกสำหรับเพย์โหลดดังกล่าว: คุณสามารถดูไบต์ดิบโดยไม่ต้องเสแสร้งว่าเป็นข้อความ การรับรู้ข้อผิดพลาดในการถอดรหัส UTF-8 เกี่ยวข้องกับการดูไบต์ในบริบท เป็นเลขคี่ที่คาดว่าจะมีลำดับหลายไบต์หรือไม่
ไบต์แรกของลำดับที่เป็นไปได้ไม่ถูกต้อง (เริ่มต้นด้วย 10xxxxxx) หรือไม่ ไบต์ต่อเนื่องหายไปหรือไม่ ปุ่มไบต์เอาท์พุตช่วยให้มีทางแยกที่สะอาดที่สุดในการตรวจสอบ หากเลขฐานสิบหกปรากฏขึ้นแต่การถอดรหัสข้อความล้มเหลว แสดงว่าการแยกวิเคราะห์ Base64 สำเร็จและเพย์โหลดเป็นไบนารี ได้รับความเสียหาย หรือเข้ารหัสด้วยชุดอักขระอื่น การเปลี่ยนเครื่องหมายวรรคตอน Base64 ไม่สามารถซ่อมแซมชุดอักขระที่ไม่ตรงกันได้หลังจากที่ไบต์ที่ถูกต้องปรากฏแล้ว
สิ่งนี้ไม่ครอบคลุมถึง - UTF-16 เพย์โหลด เอาต์พุตไบนารี เช่น รูปภาพ และการจัดการไบต์ที่ไม่ถูกต้อง
รูปแบบมีความสม่ำเสมอ 0xFF ไบต์เดียวไม่ถูกต้องใน UTF-8; ไม่สามารถเป็น ASCII ไบต์ได้ (เฉพาะ 0–127 เท่านั้นที่เป็น ASCII) และไม่สามารถเป็นไบต์นำได้ (ไบต์ตะกั่วคือ 0xC0–0xFD, 0xFF ถูกสงวนไว้) ตัวแทนเดี่ยว (แนวคิด UTF-16) ไม่สามารถปรากฏใน UTF-8; หากดูลำดับไบต์ 0xED 0xA0 0x80 (ซึ่งเข้ารหัสตัวแทน U+D800 ในรูปแบบ UTF-8) มันจะไม่ถูกต้อง UTF-8
วิธีแก้ปัญหาในอดีตวิธีหนึ่งคือ btoa(unescape(encodeURIComponent(text))) encodeURIComponent เปลี่ยน cafe ให้เป็น %C3%A9 (เปอร์เซ็นต์การเข้ารหัส UTF-8 ไบต์), unescape ทำการแพ็คใหม่เป็นหน่วยโค้ด, btoa เข้ารหัสหน่วยโค้ด วิธีนี้ใช้ได้กับข้อความส่วนใหญ่ แต่จะเปราะบางเมื่อมีตัวแทนเสมือนคนเดียวและอ่านยาก ไปป์ไลน์สมัยใหม่ - TextEncoder เป็นไบต์ จากนั้นเป็น base64 - มีความชัดเจนและเป็นมาตรฐาน TextEncoder มีอยู่ในเบราว์เซอร์สมัยใหม่ทั้งหมดและ Node.js ทำให้เป็นทางเลือกที่ถูกต้อง UTF-16 การเข้ารหัสไบต์เดียวแบบเดิมและเนื้อหาไฟล์ที่กำหนดเองจำเป็นต้องมีตัวถอดรหัสที่เลือกสำหรับไบต์เหล่านั้นหรือโปรแกรมดูที่รับรู้ไบนารี ToolAcre ไม่ได้ตั้งใจเดาในหมู่พวกเขา การคาดเดาสามารถเปลี่ยนลำดับที่ไม่ถูกต้องให้เป็นข้อความที่ทำให้เข้าใจผิดได้ ในขณะที่การถ่ายโอนข้อมูลฐานสิบหกจะรักษาทุกไบต์ไว้สำหรับการตีความที่มีข้อมูลในภายหลัง
ประเด็นสำคัญ: Base64 ให้ไบต์แก่คุณ UTF-8 ให้ข้อความแก่คุณ - ตัวเข้ารหัสและตัวถอดรหัส Base64 ทำทั้งสองขั้นตอนอย่างไรเพื่อให้ข้อความที่ถอดรหัสตรงกับอินพุตทุกประการ
เมื่อคุณมีสตริง Base64 และต้องการข้อความ UTF-8 ให้ทำตามขั้นตอนต่อไปนี้: ถอดรหัส Base64 เป็นไบต์ (โดยใช้ atob หรือไลบรารีการถอดรหัส base64) สร้าง Uint8Array จากไบต์ ส่งอาร์เรย์ไปยัง TextDecoder ด้วยชุดอักขระ utf-8 อ่านผลลัพธ์เป็นสตริง
หากอินพุตเป็นข้อมูลไบนารีแทนที่จะเป็นข้อความ ให้ข้าม TextDecoder และตรวจสอบไบต์โดยตรง ตัวเข้ารหัสและตัวถอดรหัส Base64 ให้มุมมองไบต์ฐานสิบหก โดยคงค่าที่การถอดรหัส UTF-8 ที่เข้มงวดจะปฏิเสธ ทางแยกนั้นได้รับการวินิจฉัย: ไบต์ที่สำเร็จบวกกับข้อความที่ล้มเหลวหมายถึงการแยกวิเคราะห์ Base64 ทำงานได้ ในขณะที่เพย์โหลดเป็นแบบไบนารี เสียหาย หรือเข้ารหัสด้วยชุดอักขระ เครื่องมือนี้ไม่สามารถคาดเดาได้