ฉันจะถอดรหัสสตริงที่มี Unicode ที่ใช้ Escape ได้อย่างไร


92

ฉันไม่แน่ใจว่าสิ่งนี้เรียกว่าอะไรดังนั้นฉันจึงมีปัญหาในการค้นหา ฉันจะถอดรหัสสตริงด้วย Unicode จากhttp\u00253A\u00252F\u00252Fexample.comถึงhttp://example.comด้วย JavaScript ได้อย่างไร ฉันลองunescapeแล้วdecodeURIและdecodeURIComponentฉันเดาว่าสิ่งเดียวที่เหลือคือการแทนที่สตริง

แก้ไข: ไม่ได้พิมพ์สตริง แต่เป็นสตริงย่อยจากโค้ดอื่น ดังนั้นในการแก้ปัญหาคุณต้องเริ่มจากสิ่งนี้:

var s = 'http\\u00253A\\u00252F\\u00252Fexample.com';

ฉันหวังว่าจะแสดงให้เห็นว่าทำไม unescape () ไม่ทำงาน


สตริงมาจากไหน?
Cameron

@ คาเมรอน: สตริงมาจากสคริปต์ที่ฉันเรียกว่า innerHTML เพื่อรับ นี่คือสาเหตุที่คำตอบของ alex ไม่ได้ผล
styfle

คำตอบ:


113

แก้ไข (2017-10-12) :

@MechaLynx และ @ Kevin-Weber บันทึกย่อที่unescape()เลิกใช้งานจากสภาพแวดล้อมที่ไม่ใช่เบราว์เซอร์และไม่มีอยู่ใน TypeScript decodeURIComponentเป็นการทดแทนแบบดรอปอิน เพื่อความเข้ากันได้ที่กว้างขึ้นให้ใช้ด้านล่างแทน:

decodeURIComponent(JSON.parse('"http\\u00253A\\u00252F\\u00252Fexample.com"'));
> 'http://example.com'

คำตอบเดิม:

unescape(JSON.parse('"http\\u00253A\\u00252F\\u00252Fexample.com"'));
> 'http://example.com'

คุณสามารถถ่ายโอนงานทั้งหมดไปที่ JSON.parse


7
น่าสนใจ. ฉันต้องเพิ่มคำพูดรอบ ๆ มันunescape(JSON.parse('"' + s + '"'));เหตุผลสำหรับคำพูดพิเศษคืออะไร? นั่นทำให้ JSON ถูกต้องหรือไม่
styfle

1
โปรดทราบว่าสิ่งนี้ดูเหมือนจะเร็วกว่าfromCharCodeวิธีการอย่างมาก: jsperf.com/unicode-func-vs-json-parse
nrabinowitz

17
หมายเหตุสำคัญเกี่ยวกับคำตอบของ @ styfle: อย่าใช้JSON.parse('"' + s + '"')เมื่อต้องจัดการกับข้อมูลที่ไม่น่าเชื่อถือให้ใช้JSON.parse('"' + s.replace('"', '\\"') + '"')แทนมิฉะนั้นรหัสของคุณจะพังเมื่ออินพุตมีเครื่องหมายคำพูด
ntninja

7
คำตอบที่ดี @ alexander255 แต่คุณต้องการใช้: JSON.parse ('"' + str.replace (/ \" / g, '\\ "' + '") เพื่อแทนที่ทั้งหมดที่เกิดขึ้นของอักขระนั้นตลอดช่วง สตริงแทนที่จะแทนที่
CS

2
สำหรับผู้ที่เจอปัญหานี้และกังวลเพราะunescape()ถูกเลิกใช้งานแล้วให้ใช้decodeURIComponent()งานได้เหมือนกับunescape()ในกรณีนี้ดังนั้นเพียงแค่แทนที่ด้วยสิ่งนี้และคุณก็ทำได้ดี
mechalynx

116

อัปเดต : โปรดทราบว่านี่เป็นโซลูชันที่ควรใช้กับเบราว์เซอร์รุ่นเก่าหรือแพลตฟอร์มที่ไม่ใช่เบราว์เซอร์และยังคงใช้งานได้เพื่อวัตถุประสงค์ในการเรียนการสอน โปรดดูคำตอบของ @radicand ด้านล่างสำหรับคำตอบที่เป็นปัจจุบันเพิ่มเติม


นี่คือสตริง Unicode ที่ใช้ Escape ก่อนอื่นสตริงจะถูกหลีกเลี่ยงจากนั้นเข้ารหัสด้วย Unicode ในการแปลงกลับเป็นปกติ:

var x = "http\\u00253A\\u00252F\\u00252Fexample.com";
var r = /\\u([\d\w]{4})/gi;
x = x.replace(r, function (match, grp) {
    return String.fromCharCode(parseInt(grp, 16)); } );
console.log(x);  // http%3A%2F%2Fexample.com
x = unescape(x);
console.log(x);  // http://example.com

ที่จะอธิบาย: \u0025ผมใช้การแสดงออกปกติที่จะมองหา 0025อย่างไรก็ตามเนื่องจากฉันต้องการเพียงส่วนหนึ่งของสายนี้สำหรับการดำเนินการแทนที่ของฉันฉันใช้วงเล็บเพื่อแยกส่วนผมจะนำมาใช้ใหม่ ส่วนที่แยกนี้เรียกว่ากลุ่ม

giส่วนหนึ่งในตอนท้ายของการแสดงออกหมายถึงว่ามันควรจะตรงกับทุกกรณีในสายที่ไม่เพียง แต่ครั้งแรกหนึ่งและการจับคู่ที่ควรจะเป็นกรณีตาย สิ่งนี้อาจดูไม่จำเป็นในตัวอย่าง แต่เพิ่มความคล่องตัว

ตอนนี้ในการแปลงจากสตริงหนึ่งไปเป็นสตริงถัดไปฉันต้องดำเนินการขั้นตอนบางอย่างในแต่ละกลุ่มของการแข่งขันแต่ละรายการและฉันไม่สามารถทำได้โดยเพียงแค่เปลี่ยนสตริง เป็นประโยชน์การดำเนินการ String.replace สามารถยอมรับฟังก์ชันซึ่งจะดำเนินการสำหรับการแข่งขันแต่ละครั้ง การกลับมาของฟังก์ชันนั้นจะแทนที่การจับคู่ในสตริง

ฉันใช้พารามิเตอร์ตัวที่สองที่ฟังก์ชันนี้ยอมรับซึ่งเป็นกลุ่มที่ฉันต้องใช้และแปลงเป็นลำดับ utf-8 ที่เทียบเท่าจากนั้นใช้unescapeฟังก์ชันในตัวเพื่อถอดรหัสสตริงให้อยู่ในรูปแบบที่เหมาะสม


3
ขอบคุณ. ช่วยอธิบายหน่อยได้ไหมว่าคุณกำลังทำอะไรอยู่ ดูเหมือนว่า regex กำลังมองหา\uคำนำหน้าและมากกว่าตัวเลขฐานสิบหก 4 อักขระ (ตัวอักษรหรือตัวเลข) ฟังก์ชันในวิธีการแทนที่ทำงานอย่างไร
สไตล์

1
คุณพูดถูกต้องการคำอธิบายดังนั้นฉันจึงอัปเดตโพสต์ของฉัน สนุก!
Ioannis Karadimas

1
ทางออกที่ดี ในกรณีของฉันฉันกำลังเข้ารหัสอักขระสากล (ที่ไม่ใช่ ascii) ทั้งหมดที่ถูกส่งจากเซิร์ฟเวอร์เป็น Unicode ที่ไม่ได้รับการยกเว้นจากนั้นใช้ฟังก์ชันของคุณในเบราว์เซอร์เพื่อถอดรหัสอักขระให้เป็นอักขระ UTF-8 ที่ถูกต้อง ฉันพบว่าฉันต้องอัปเดต regex ต่อไปนี้เพื่อที่จะจับอักขระจากทุกภาษา (เช่นไทย):var r = /\\u([\d\w]{1,})/gi;
Nathan Hanna

2
โปรดทราบว่าสิ่งนี้ดูเหมือนจะช้ากว่าJSON.parseวิธีการอย่างมาก: jsperf.com/unicode-func-vs-json-parse
nrabinowitz

1
@IoannisKaradimas แน่นอนที่สุดคือการเลิกใช้งานใน Javascript การอ้างว่าและสนับสนุนโดยระบุว่าเบราว์เซอร์รุ่นเก่าจะต้องได้รับการสนับสนุนอยู่เสมอนั้นเป็นมุมมองทางประวัติศาสตร์ที่สมบูรณ์ ไม่ว่าในกรณีใดใครก็ตามที่ต้องการใช้สิ่งนี้และต้องการหลีกเลี่ยงunescape()สามารถใช้decodeURIComponent()แทนได้ มันทำงานเหมือนกันในกรณีนี้ ฉันอยากจะแนะนำวิธีการของ radicand เนื่องจากง่ายกว่าเช่นเดียวกับที่รองรับและดำเนินการได้เร็วกว่าพร้อมผลลัพธ์เดียวกัน (อย่าลืมอ่านความคิดเห็น)
mechalynx

21

โปรดทราบว่าการใช้งานunescape()นั้นเลิกใช้แล้วและใช้ไม่ได้กับคอมไพลเลอร์ TypeScript เป็นต้น

จากคำตอบของ radicand และส่วนความคิดเห็นด้านล่างนี่คือโซลูชันที่อัปเดต:

var string = "http\\u00253A\\u00252F\\u00252Fexample.com";
decodeURIComponent(JSON.parse('"' + string.replace(/\"/g, '\\"') + '"'));

http://example.com


วิธีนี้ใช้ไม่ได้กับบางสตริงเนื่องจากเครื่องหมายคำพูดสามารถทำลายสตริง JSON และส่งผลให้เกิดข้อผิดพลาดในการแยกวิเคราะห์ JSON ฉันใช้คำตอบอื่น ( stackoverflow.com/a/7885499/249327 ) ในกรณีเหล่านี้
nickdos

2

ฉันมีตัวแทนไม่เพียงพอที่จะใส่สิ่งนี้ไว้ใต้ความคิดเห็นสำหรับคำตอบที่มีอยู่:

unescapeเลิกใช้งานเฉพาะกับการทำงานกับ URI (หรือ utf-8 ที่เข้ารหัส) ซึ่งอาจเป็นไปตามความต้องการของคนส่วนใหญ่ encodeURIComponentแปลงสตริง js เป็น UTF-8 ที่ใช้ Escape และdecodeURIComponentใช้ได้เฉพาะกับ UTF-8 ไบต์ที่ใช้ Escape มันแสดงข้อผิดพลาดสำหรับบางสิ่งเช่นdecodeURIComponent('%a9'); // errorเนื่องจาก ascii ขยายไม่ใช่ utf-8 ที่ถูกต้อง (แม้ว่าจะยังคงเป็นค่ายูนิโคด) ในขณะที่unescape('%a9'); // ©คุณจำเป็นต้องรู้ข้อมูลของคุณเมื่อใช้ decodeURIComponent

decodeURIComponent จะไม่ทำงาน"%C2"หรือไบต์เดี่ยว ๆ0x7fเพราะใน utf-8 นั้นระบุว่าเป็นส่วนหนึ่งของตัวแทน อย่างไรก็ตามdecodeURIComponent("%C2%A9") //gives you ©Unescape ทำงานไม่ถูกต้อง// ©และจะไม่เกิดข้อผิดพลาดดังนั้น Unescape อาจนำไปสู่รหัสบั๊กหากคุณไม่ทราบข้อมูลของคุณ


1

การใช้JSON.decodeสิ่งนี้มาพร้อมกับข้อเสียที่สำคัญที่คุณต้องระวัง:

  • คุณต้องตัดสตริงด้วยเครื่องหมายคำพูดคู่
  • ตัวละครหลายตัวไม่ได้รับการสนับสนุนและต้องหลบหนีเอง ยกตัวอย่างเช่นที่ผ่านใด ๆ ต่อไปนี้เพื่อJSON.decode(หลังจากการตัดพวกเขาในราคาคู่) จะเกิดข้อผิดพลาดแม้ว่าเหล่านี้ทั้งหมดที่ถูกต้อง: \\n, \n, \\0,a"a
  • ไม่รองรับการหลบหนีเลขฐานสิบหก: \\x45
  • ไม่สนับสนุนลำดับจุดรหัส Unicode: \\u{045}

มีข้อแม้อื่น ๆ เช่นกัน โดยพื้นฐานแล้วการใช้JSON.decodeเพื่อจุดประสงค์นี้เป็นการแฮ็กและไม่ได้ผลอย่างที่คุณคาดหวังเสมอไป คุณควรใช้JSONไลบรารีเพื่อจัดการ JSON ไม่ใช่สำหรับการทำงานของสตริง


ฉันเพิ่งพบปัญหานี้ด้วยตัวเองและต้องการตัวถอดรหัสที่มีประสิทธิภาพดังนั้นฉันจึงลงเอยด้วยการเขียนตัวเอง มันเป็นที่สมบูรณ์และการทดสอบอย่างละเอียดและสามารถใช้ได้ที่นี่: https://github.com/iansan5653/unraw มันเลียนแบบมาตรฐาน JavaScript ได้ใกล้เคียงที่สุด

คำอธิบาย:

ซอร์สมีประมาณ 250 บรรทัดดังนั้นฉันจะไม่รวมทั้งหมดไว้ที่นี่ แต่โดยพื้นฐานแล้วจะใช้ Regex ต่อไปนี้เพื่อค้นหาลำดับการหลีกเลี่ยงทั้งหมดจากนั้นแยกวิเคราะห์โดยใช้parseInt(string, 16)เพื่อถอดรหัสตัวเลขฐาน 16 จากนั้นจึงString.fromCodePoint(number)จะได้อักขระที่เกี่ยวข้อง:

/\\(?:(\\)|x([\s\S]{0,2})|u(\{[^}]*\}?)|u([\s\S]{4})\\u([^{][\s\S]{0,3})|u([\s\S]{0,4})|([0-3]?[0-7]{1,2})|([\s\S])|$)/g

แสดงความคิดเห็น (หมายเหตุ: regex นี้ตรงกับลำดับการหลีกเลี่ยงทั้งหมดรวมถึงลำดับที่ไม่ถูกต้องหากสตริงจะส่งข้อผิดพลาดใน JS มันจะแสดงข้อผิดพลาดในไลบรารีของฉัน [เช่น'\x!!'จะผิดพลาด]):

/
\\ # All escape sequences start with a backslash
(?: # Starts a group of 'or' statements
(\\) # If a second backslash is encountered, stop there (it's an escaped slash)
| # or
x([\s\S]{0,2}) # Match valid hexadecimal sequences
| # or
u(\{[^}]*\}?) # Match valid code point sequences
| # or
u([\s\S]{4})\\u([^{][\s\S]{0,3}) # Match surrogate code points which get parsed together
| # or
u([\s\S]{0,4}) # Match non-surrogate Unicode sequences
| # or
([0-3]?[0-7]{1,2}) # Match deprecated octal sequences
| # or
([\s\S]) # Match anything else ('.' doesn't match newlines)
| # or
$ # Match the end of the string
) # End the group of 'or' statements
/g # Match as many instances as there are

ตัวอย่าง

การใช้ห้องสมุดนั้น:

import unraw from "unraw";

let step1 = unraw('http\\u00253A\\u00252F\\u00252Fexample.com');
// yields "http%3A%2F%2Fexample.com"
// Then you can use decodeURIComponent to further decode it:
let step2 = decodeURIComponent(step1);
// yields http://example.com
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.