Làm cách nào để giải mã một chuỗi với unicode thoát?


89

Tôi không chắc cái này được gọi là gì nên tôi đang gặp khó khăn khi tìm kiếm nó. Làm cách nào để giải mã một chuỗi bằng unicode từ http\u00253A\u00252F\u00252Fexample.comsang http://example.combằng JavaScript? Tôi đã thử unescape, decodeURIdecodeURIComponentvì vậy tôi đoán điều duy nhất còn lại là thay thế chuỗi.

CHỈNH SỬA: Chuỗi không được nhập mà là một chuỗi con từ một đoạn mã khác. Vì vậy, để giải quyết vấn đề, bạn phải bắt đầu với một cái gì đó như sau:

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

Tôi hy vọng điều đó cho thấy tại sao unescape () không hoạt động.


Chuỗi đến từ đâu?
Cameron

@Cameron: Chuỗi là từ một tập lệnh mà tôi gọi là innerHTML để lấy. Đây là lý do tại sao câu trả lời của alex không hoạt động.
styfle

Câu trả lời:


109

Chỉnh sửa (2017-10-12) :

@MechaLynx và @ Kevin-Weber lưu ý rằng unescape()nó không được chấp nhận trong môi trường không phải trình duyệt và không tồn tại trong TypeScript. decodeURIComponentlà một sự thay thế thả vào. Để có khả năng tương thích rộng hơn, hãy sử dụng phần bên dưới để thay thế:

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

Câu trả lời ban đầu:

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

Bạn có thể giảm tải tất cả công việc để JSON.parse


6
Hấp dẫn. Tôi đã phải thêm dấu ngoặc kép xung quanh nó unescape(JSON.parse('"' + s + '"'));Lý do cho dấu ngoặc kép là gì? Điều đó có làm cho nó trở thành JSON hợp lệ không?
súng trường vào

1
Lưu ý rằng xuất hiện này là nhanh hơn so với cách đáng kể fromCharCodecách tiếp cận: jsperf.com/unicode-func-vs-json-parse
nrabinowitz

17
Lưu ý quan trọng về câu trả lời của @ styfle: Thay vào đó, không sử dụng JSON.parse('"' + s + '"')khi xử lý việc sử dụng dữ liệu không đáng tin cậy JSON.parse('"' + s.replace('"', '\\"') + '"'), nếu không mã của bạn sẽ bị hỏng khi đầu vào chứa dấu ngoặc kép.
ntninja

7
Câu trả lời tuyệt vời @ alexander255, nhưng bạn thực sự muốn sử dụng: JSON.parse ('"' + str.replace (/ \" / g, '\\ "' + '"') để thay thế TẤT CẢ các lần xuất hiện của ký tự đó trong suốt chuỗi, thay vì thay thế một chuỗi.
CS

2
Đối với những người gặp phải điều này và lo lắng vì unescape()đã không được chấp nhận, decodeURIComponent()hoạt động giống hệt như unescape()trong trường hợp này, vì vậy chỉ cần thay thế nó bằng điều đó và bạn đã tốt.
Mechalynx.

116

CẬP NHẬT : Xin lưu ý rằng đây là giải pháp sẽ áp dụng cho các trình duyệt cũ hơn hoặc các nền tảng không phải trình duyệt và được duy trì cho các mục đích hướng dẫn. Vui lòng tham khảo câu trả lời của @radicand bên dưới để có câu trả lời cập nhật hơn.


Đây là một unicode, chuỗi thoát. Đầu tiên chuỗi được thoát, sau đó được mã hóa bằng unicode. Để chuyển đổi trở lại bình thường:

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

Để giải thích: Tôi sử dụng một biểu thức chính quy để tìm kiếm \u0025. Tuy nhiên, vì tôi chỉ cần một phần của chuỗi này cho hoạt động thay thế của mình, tôi sử dụng dấu ngoặc đơn để tách biệt phần mà tôi sẽ sử dụng lại , 0025. Phần biệt lập này được gọi là nhóm.

Phần giở cuối biểu thức biểu thị nó phải khớp với tất cả các trường hợp trong chuỗi, không chỉ trường hợp đầu tiên và phần khớp phải không phân biệt chữ hoa chữ thường. Điều này có thể trông không cần thiết với ví dụ, nhưng nó bổ sung tính linh hoạt.

Bây giờ, để chuyển đổi từ một chuỗi sang chuỗi tiếp theo, tôi cần thực hiện một số bước trên mỗi nhóm của mỗi trận đấu và tôi không thể làm điều đó bằng cách chỉ đơn giản là chuyển đổi chuỗi. Một cách hữu ích, hoạt động String.replace có thể chấp nhận một hàm, hàm này sẽ được thực thi cho mỗi trận đấu. Việc trả về của hàm đó sẽ thay thế chính kết quả khớp trong chuỗi.

Tôi sử dụng tham số thứ hai mà hàm này chấp nhận, là nhóm mà tôi cần sử dụng và biến đổi nó thành chuỗi utf-8 tương đương, sau đó sử dụng unescapehàm tích hợp để giải mã chuỗi về dạng thích hợp của nó.


3
Cảm ơn. Bạn có thể giải thích một chút về những gì bạn đang làm? Có vẻ như regex đang tìm kiếm một \utiền tố và hơn một số hex 4 ký tự (chữ cái hoặc số). Hàm trong phương thức Replace hoạt động như thế nào?
styfle

1
Bạn nói đúng, điều đó cần một lời giải thích, vì vậy tôi đã cập nhật bài đăng của mình. Thưởng thức!
Ioannis Karadimas

1
Giải pháp tuyệt vời. Trong trường hợp của tôi, tôi đang mã hóa tất cả các ký tự quốc tế (không phải ascii) được gửi từ máy chủ dưới dạng unicode thoát, sau đó sử dụng chức năng của bạn trong trình duyệt để giải mã các ký tự thành ký tự UTF-8 chính xác. Tôi thấy rằng tôi phải cập nhật regex sau để bắt các ký tự từ tất cả các ngôn ngữ (tức là tiếng Thái):var r = /\\u([\d\w]{1,})/gi;
Nathan Hanna

2
Lưu ý rằng điều này dường như chậm hơn đáng kể so với JSON.parsecách tiếp cận: jsperf.com/unicode-func-vs-json-parse
nrabinowitz

1
@IoannisKaradimas Chắc chắn có điều gì đó như là sự không dùng nữa trong Javascript. Tuyên bố điều đó và sau đó hỗ trợ nó bằng cách tuyên bố rằng các trình duyệt cũ hơn phải luôn được hỗ trợ là một quan điểm hoàn toàn mang tính lịch sử. Trong mọi trường hợp, bất cứ ai muốn sử dụng điều này và cũng muốn tránh unescape()có thể sử dụng decodeURIComponent()thay thế. Nó hoạt động giống hệt nhau trong trường hợp này. Tuy nhiên, tôi muốn giới thiệu cách tiếp cận của radicand, vì nó đơn giản hơn, được hỗ trợ và thực thi nhanh hơn, với cùng kết quả (tuy nhiên, hãy nhớ đọc các nhận xét).
mechalynx

21

Lưu ý rằng việc sử dụng unescape()được phản và không làm việc với các trình biên dịch nguyên cảo, ví dụ.

Dựa trên câu trả lời của radicand và phần nhận xét bên dưới, đây là giải pháp được cập nhật:

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

http://example.com


Điều này không hoạt động đối với một số chuỗi, vì dấu ngoặc kép có thể phá vỡ chuỗi JSON và dẫn đến lỗi phân tích cú pháp JSON. Tôi đã sử dụng câu trả lời khác ( stackoverflow.com/a/7885499/249327 ) trong những trường hợp này.
nickdos

2

Tôi không có đủ đại diện để đưa điều này vào phần nhận xét cho các câu trả lời hiện có:

unescapechỉ không được chấp nhận để làm việc với URI (hoặc bất kỳ utf-8 được mã hóa nào), đây có lẽ là trường hợp cho nhu cầu của hầu hết mọi người. encodeURIComponentchuyển đổi một chuỗi js thành UTF-8 thoát và decodeURIComponentchỉ hoạt động trên các byte UTF-8 đã thoát. Nó gây ra lỗi cho một cái gì đó như decodeURIComponent('%a9'); // errorvì ascii mở rộng không hợp lệ utf-8 (mặc dù đó vẫn là một giá trị unicode), trong khi unescape('%a9'); // ©Vì vậy, bạn cần biết dữ liệu của mình khi sử dụng decodeURIComponent.

decodeURIComponent sẽ không hoạt động trên "%C2"hoặc bất kỳ byte đơn lẻ nào hết 0x7fvì trong utf-8 chỉ ra một phần của đại diện. Tuy nhiên decodeURIComponent("%C2%A9") //gives you ©Unescape sẽ không hoạt động bình thường trên đó // ©VÀ nó sẽ không gây ra lỗi, vì vậy unescape có thể dẫn đến mã lỗi nếu bạn không biết dữ liệu của mình.


1

Sử dụng JSON.decodecho mục đích này đi kèm với những hạn chế đáng kể mà bạn phải biết:

  • Bạn phải đặt chuỗi trong dấu ngoặc kép
  • Nhiều ký tự không được hỗ trợ và phải tự thoát ra. Ví dụ, đi qua bất kỳ những điều sau đây để JSON.decode(sau khi gói chúng trong dấu ngoặc kép) sẽ báo lỗi mặc dù đây là những bài hợp lệ: \\n, \n, \\0,a"a
  • Nó không hỗ trợ thoát hệ thập lục phân: \\x45
  • Nó không hỗ trợ chuỗi điểm mã Unicode: \\u{045}

Cũng có những lưu ý khác. Về cơ bản, sử dụng JSON.decodecho mục đích này là một cuộc tấn công và không hoạt động theo cách bạn có thể mong đợi. Bạn nên gắn bó với việc sử dụng JSONthư viện để xử lý JSON, không phải cho các hoạt động chuỗi.


Gần đây tôi đã tự mình gặp phải vấn đề này và muốn có một bộ giải mã mạnh mẽ, vì vậy tôi đã tự viết một bộ. Nó đã hoàn chỉnh và được kiểm tra kỹ lưỡng và có sẵn tại đây: https://github.com/iansan5653/unraw . Nó bắt chước tiêu chuẩn JavaScript càng gần càng tốt.

Giải trình:

Nguồn có khoảng 250 dòng vì vậy tôi sẽ không bao gồm tất cả ở đây, nhưng về cơ bản nó sử dụng Regex sau đây để tìm tất cả các chuỗi thoát và sau đó phân tích cú pháp chúng bằng cách sử dụng parseInt(string, 16)để giải mã các số cơ số 16 và sau đó String.fromCodePoint(number)để lấy ký tự tương ứng:

/\\(?:(\\)|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

Đã nhận xét (LƯU Ý: regex này khớp với tất cả các chuỗi thoát, bao gồm cả những chuỗi không hợp lệ. Nếu chuỗi sẽ gây ra lỗi trong JS, nó sẽ tạo ra lỗi trong thư viện của tôi [tức là '\x!!'sẽ lỗi]):

/
\\ # 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

Thí dụ

Sử dụng thư viện đó:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.