Bagaimana cara memecahkan kode string dengan escape unicode?

92

Saya tidak yakin apa namanya, jadi saya kesulitan mencarinya. Bagaimana cara memecahkan kode string dengan unicode from http\u00253A\u00252F\u00252Fexample.comto http://example.comdengan JavaScript? Saya mencoba unescape, decodeURIdan decodeURIComponentjadi saya kira satu-satunya hal kiri string menggantikan.

EDIT: String tidak diketik, melainkan substring dari bagian kode lain. Jadi untuk menyelesaikan masalah, Anda harus memulai dengan sesuatu seperti ini:

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

Saya harap itu menunjukkan mengapa unescape () tidak berfungsi.

styfle
sumber
Dari mana asalnya senar itu?
Cameron
@ Kameron: String ini dari skrip yang saya sebut innerHTML untuk mendapatkannya. Inilah mengapa jawaban alex tidak berhasil.
styfle

Jawaban:

113

Edit (2017-10-12) :

Catatan @MechaLynx dan @ Kevin-Weber unescape()sudah usang dari lingkungan non-browser dan tidak ada di TypeScript. decodeURIComponentadalah pengganti drop-in. Untuk kompatibilitas yang lebih luas, gunakan yang di bawah ini sebagai gantinya:

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

Jawaban asli:

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

Anda dapat memindahkan semua pekerjaan ke JSON.parse

radian
sumber
7
Menarik. Saya memang harus menambahkan kutipan di sekitarnya. unescape(JSON.parse('"' + s + '"'));Apa alasan untuk kutipan tambahan? Apakah itu membuat JSON valid?
styfle
1
Perhatikan bahwa ini tampaknya jauh lebih cepat daripada fromCharCodependekatan: jsperf.com/unicode-func-vs-json-parse
nrabinowitz
17
Catatan penting tentang jawaban @ styfle: Jangan gunakan JSON.parse('"' + s + '"')saat berurusan dengan penggunaan data yang tidak tepercaya JSON.parse('"' + s.replace('"', '\\"') + '"'), jika tidak, kode Anda akan rusak saat input berisi tanda kutip.
ntninja
7
Jawaban bagus @ alexander255, tetapi Anda sebenarnya ingin menggunakan: JSON.parse ('"' + str.replace (/ \" / g, '\\ "' + '"') untuk menggantikan SEMUA kemunculan karakter itu di seluruh string, bukan mengganti satu.
CS
2
Bagi mereka yang menemukan ini dan khawatir karena unescape()sudah usang, decodeURIComponent()berfungsi sama unescape()dalam kasus ini, jadi gantilah dengan itu dan Anda baik-baik saja.
mechalynx
116

PEMBARUAN : Harap dicatat bahwa ini adalah solusi yang harus diterapkan ke browser lama atau platform non-browser, dan tetap hidup untuk tujuan instruksional. Silakan merujuk ke jawaban @radicand di bawah ini untuk jawaban yang lebih terkini.


Ini adalah unicode, string yang di-escape. Pertama, string itu lolos, lalu dikodekan dengan unicode. Untuk mengubah kembali ke normal:

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

Untuk menjelaskan: Saya menggunakan ekspresi reguler untuk dicari \u0025. Namun, karena saya hanya memerlukan sebagian dari string ini untuk operasi penggantian, saya menggunakan tanda kurung untuk mengisolasi bagian yang akan saya gunakan kembali , 0025. Bagian yang terisolasi ini disebut grup.

Bagian gidi akhir ekspresi menunjukkan bahwa itu harus cocok dengan semua contoh dalam string, bukan hanya yang pertama, dan pencocokan harus peka huruf besar / kecil. Ini mungkin terlihat tidak perlu diberikan contohnya, tetapi ini menambah keserbagunaan.

Sekarang, untuk mengonversi dari satu string ke string berikutnya, saya perlu menjalankan beberapa langkah pada setiap grup dari setiap pertandingan, dan saya tidak dapat melakukannya hanya dengan mengubah string. Bermanfaat, operasi String.replace dapat menerima sebuah fungsi, yang akan dijalankan untuk setiap pertandingan. Kembalinya fungsi itu akan menggantikan kecocokan itu sendiri dalam string.

Saya menggunakan parameter kedua yang diterima fungsi ini, yang merupakan grup yang perlu saya gunakan, dan mengubahnya menjadi urutan utf-8 yang setara, kemudian menggunakan unescapefungsi bawaan untuk memecahkan kode string ke bentuk yang tepat.

Ioannis Karadimas
sumber
3
Terima kasih. Bisakah Anda menjelaskan sedikit tentang apa yang Anda lakukan? Sepertinya regex mencari \uawalan dan bukan angka hex 4 karakter (huruf atau angka). Bagaimana cara kerja fungsi dalam metode ganti?
styfle
1
Anda benar, itu membutuhkan penjelasan, jadi saya telah memperbarui posting saya. Nikmati!
Ioannis Karadimas
1
Solusi bagus. Dalam kasus saya, saya menyandikan semua karakter internasional (non-ascii) yang dikirim dari server sebagai unicode lolos, kemudian menggunakan fungsi Anda di browser untuk mendekode karakter ke karakter UTF-8 yang benar. Saya menemukan bahwa saya harus memperbarui regex berikut untuk menangkap karakter dari semua bahasa (yaitu Thailand):var r = /\\u([\d\w]{1,})/gi;
Nathan Hanna
2
Perhatikan bahwa ini tampaknya jauh lebih lambat daripada JSON.parsependekatan: jsperf.com/unicode-func-vs-json-parse
nrabinowitz
1
@IoannisKaradimas Pasti ada yang namanya deprecation di Javascript. Untuk mengklaim itu dan kemudian mendukungnya dengan menyatakan bahwa browser lama harus selalu didukung adalah perspektif yang benar-benar ahistoris. Bagaimanapun, siapa pun yang ingin menggunakan ini dan juga ingin menghindari unescape()dapat menggunakan decodeURIComponent()sebagai gantinya. Ini bekerja secara identik dalam kasus ini. Saya akan merekomendasikan pendekatan radicand, karena lebih sederhana, sama seperti didukung dan lebih cepat untuk dieksekusi, dengan hasil yang sama (pastikan untuk membaca komentar bagaimanapun).
mechalynx
21

Perhatikan bahwa penggunaan dari unescape()sudah tidak digunakan lagi dan tidak berfungsi dengan compiler TypeScript, misalnya.

Berdasarkan jawaban radicand dan bagian komentar di bawah, inilah solusi yang diperbarui:

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

http://example.com

Kevin Weber
sumber
Ini tidak berfungsi untuk beberapa string, karena tanda kutip dapat merusak string JSON dan mengakibatkan kesalahan penguraian JSON. Saya menggunakan jawaban lain ( stackoverflow.com/a/7885499/249327 ) dalam kasus ini.
nickdos
2

Saya tidak memiliki cukup perwakilan untuk meletakkan ini di bawah komentar untuk jawaban yang ada:

unescapehanya tidak digunakan lagi untuk bekerja dengan URI (atau utf-8 yang dikodekan apa pun) yang mungkin merupakan kasus untuk kebutuhan kebanyakan orang. encodeURIComponentmengonversi string js menjadi UTF-8 yang di-escape dan decodeURIComponenthanya berfungsi pada byte UTF-8 yang di-escape. Itu melempar kesalahan untuk sesuatu seperti decodeURIComponent('%a9'); // errorkarena ascii diperpanjang tidak valid utf-8 (meskipun itu masih nilai unicode), sedangkanunescape('%a9'); // © Jadi Anda perlu mengetahui data Anda saat menggunakan decodeURIComponent.

decodeURIComponent tidak akan berfungsi "%C2"atau byte tunggal apa pun di atas 0x7fkarena di utf-8 yang menunjukkan bagian dari pengganti. Namun decodeURIComponent("%C2%A9") //gives you ©Unescape tidak akan berfungsi dengan baik pada itu // ©DAN itu tidak akan menimbulkan kesalahan, jadi unescape dapat menyebabkan kode buggy jika Anda tidak tahu data Anda.

aamarks
sumber
1

Menggunakan JSON.decodeuntuk ini disertai dengan kekurangan signifikan yang harus Anda waspadai:

  • Anda harus membungkus string dengan tanda kutip ganda
  • Banyak karakter yang tidak didukung dan harus di-escape sendiri. Misalnya, lewat salah satu berikut untuk JSON.decode(setelah membungkus mereka dalam tanda kutip ganda) akan error meskipun ini semua berlaku: \\n, \n, \\0,a"a
  • Itu tidak mendukung pelarian heksadesimal: \\x45
  • Itu tidak mendukung urutan titik kode Unicode: \\u{045}

Ada peringatan lain juga. Pada dasarnya, menggunakan JSON.decodeuntuk tujuan ini adalah peretasan dan tidak berfungsi seperti yang selalu Anda harapkan. Anda harus tetap menggunakan JSONpustaka untuk menangani JSON, bukan untuk operasi string.


Saya baru-baru ini mengalami masalah ini sendiri dan menginginkan decoder yang kuat, jadi saya akhirnya menulisnya sendiri. Ini lengkap dan benar-benar diuji dan tersedia di sini: https://github.com/iansan5653/unraw . Ini meniru standar JavaScript sedekat mungkin.

Penjelasan:

Sumbernya sekitar 250 baris jadi saya tidak akan menyertakan semuanya di sini, tetapi pada dasarnya ia menggunakan Regex berikut untuk menemukan semua urutan pelarian dan kemudian menguraikannya menggunakan parseInt(string, 16)untuk memecahkan kode angka basis-16 dan kemudian String.fromCodePoint(number)untuk mendapatkan karakter yang sesuai:

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

Berkomentar (CATATAN: Regex ini cocok dengan semua urutan pelolosan, termasuk yang tidak valid. Jika string akan membuat kesalahan di JS, itu akan membuat kesalahan di perpustakaan saya [yaitu, '\x!!'akan salah]):

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

Contoh

Menggunakan perpustakaan itu:

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
Ian
sumber