Kaçan unicode içeren bir dizenin kodunu nasıl çözerim?


92

Buna ne denildiğinden emin değilim, bu yüzden arama konusunda sorun yaşıyorum. Nasıl gelen unicode bir dize deşifre edebilir http\u00253A\u00252F\u00252Fexample.comiçin http://example.comJavaScript? Denedim unescape, decodeURIve decodeURIComponentbu yüzden tek şey sol dize yerine sanırım.

DÜZENLEME: Dize yazılmamış, başka bir kod parçasından bir alt dizedir. Yani sorunu çözmek için şuna benzer bir şeyle başlamalısınız:

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

Umarım bu unescape () neden çalışmadığını gösterir.


İp nereden geliyor?
Cameron

@Cameron: Dizgi almak için innerHTML adını verdiğim bir betikten. Alex'in cevabı bu yüzden işe yaramıyor.
styfle

Yanıtlar:


113

Düzenleme (2017-10-12) :

@MechaLynx ve @ Kevin-Weber unescape(), tarayıcı olmayan ortamlardan kaldırıldığını ve TypeScript'te mevcut olmadığını not eder. decodeURIComponentbir drop-in yedeğidir. Daha geniş uyumluluk için bunun yerine aşağıdakileri kullanın:

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

Orijinal cevap:

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

Tüm işi boşaltabilirsiniz JSON.parse


7
İlginç. Etrafına alıntılar eklemek zorunda kaldım unescape(JSON.parse('"' + s + '"'));Fazladan alıntıların nedeni nedir? Bu onu geçerli bir JSON yapar mı?
styfle

1
Bunun fromCharCodeyaklaşımdan önemli ölçüde daha hızlı göründüğüne dikkat edin : jsperf.com/unicode-func-vs-json-parse
nrabinowitz 14

17
@ Styfle'ın cevabı hakkında önemli not: Bunun yerine JSON.parse('"' + s + '"')güvenilir olmayan veri kullanımıyla uğraşırken kullanmayın JSON.parse('"' + s.replace('"', '\\"') + '"'), aksi takdirde giriş tırnak işaretleri içerdiğinde kodunuz kırılır .
ntninja

7
Harika cevap @ alexander255, ama aslında şu karakterin TÜM tekrarlarını değiştirmek için JSON.parse ('"' + str.replace (/ \" / g, '\\ "' + '"') kullanmak isteyeceksiniz. dize, yerine bir tane.
CS

2
Bununla karşılaşan ve unescape()kullanımdan kaldırıldığı için endişelenenler decodeURIComponent()için unescape(), bu durumda aynı şekilde çalışır , bu yüzden sadece bununla değiştirin ve iyisiniz.
mechalynx

116

GÜNCELLEME : Lütfen bunun eski tarayıcılar veya tarayıcı olmayan platformlar için geçerli olması gereken bir çözüm olduğunu ve eğitim amacıyla canlı tutulması gerektiğini unutmayın. Daha güncel bir yanıt için lütfen @radicand'ın aşağıdaki yanıtına bakın.


Bu bir unicode, çıkış karakterli dizedir. Önce dizeden çıkış yapıldı, ardından unicode ile kodlandı. Normale dönmek için:

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

Açıklamak gerekirse: Aramak için normal bir ifade kullanıyorum \u0025. Ancak, değiştirme işlemim için bu dizenin yalnızca bir kısmına ihtiyacım olduğundan, yeniden kullanacağım parçayı izole etmek için parantez kullanıyorum 0025. Bu izole bölüme grup adı verilir.

giİfadenin sonuna parçası o dizede sadece ilkini tüm örneklerini aynı olmalıdır gösterir ve eşleşen harf duyarsız olması gerektiğini söyledi. Örneğe bakıldığında bu gereksiz görünebilir, ancak çok yönlülük ekler.

Şimdi, bir dizeden diğerine dönüştürmek için, her eşleşmenin her bir grubu için bazı adımlar uygulamam gerekiyor ve bunu sadece dizeyi dönüştürerek yapamam. Yararlı bir şekilde, String.replace işlemi her eşleşme için yürütülecek bir işlevi kabul edebilir. Bu işlevin dönüşü, dizedeki eşleşmenin kendisini değiştirecektir.

Bu işlevin kabul ettiği ikinci parametreyi, yani kullanmam gereken grubu kullanıyorum ve onu eşdeğer utf-8 dizisine dönüştürüyorum, ardından unescapedizeyi uygun biçimine çözmek için yerleşik işlevi kullanıyorum.


3
Teşekkürler. Ne yaptığınızı biraz açıklayabilir misiniz? Görünüşe göre normal ifade bir \uön ek ve 4 karakterli onaltılık bir sayı (harfler veya sayılar) arıyor . Değiştirme yöntemindeki işlev nasıl çalışır?
styfle

1
Haklısın, bunun bir açıklamaya ihtiyacı vardı, bu yüzden yazımı güncelledim. Zevk almak!
Ioannis Karadimas

1
Harika çözüm. Benim durumumda, sunucudan gönderilen tüm uluslararası (ascii olmayan) karakterleri çıkışlı unicode olarak kodluyorum, ardından karakterleri doğru UTF-8 karakterlerine çözmek için tarayıcıdaki işlevinizi kullanıyorum. Tüm dillerdeki (yani Tayca) karakterleri yakalamak için aşağıdaki normal ifadeyi güncellemem gerektiğini fark ettim:var r = /\\u([\d\w]{1,})/gi;
Nathan Hanna

2
Bunun JSON.parseyaklaşımdan önemli ölçüde daha yavaş göründüğüne dikkat edin : jsperf.com/unicode-func-vs-json-parse
nrabinowitz 14

1
@IoannisKaradimas Javascript'te kesinlikle kullanımdan kaldırma gibi bir şey var. Bunu iddia etmek ve daha sonra eski tarayıcıların her zaman desteklenmesi gerektiğini belirterek desteklemek tamamen tarih dışı bir bakış açısıdır. Her durumda, bunu kullanmak isteyen ve bundan kaçınmak isteyen herkes unescape()kullanabilir decodeURIComponent(). Bu durumda aynı şekilde çalışır. Radicand'ın yaklaşımını tavsiye ederim, çünkü daha basit, desteklendiği kadar ve uygulaması daha hızlı, aynı sonuçlarla (ancak yorumları okuduğunuzdan emin olun).
mechalynx

21

Not olduğu kullanımının unescape()olduğu kullanımdan kaldırıldı ve örneğin typescript derleyici ile çalışmaz.

Radicand'ın cevabına ve aşağıdaki yorumlar bölümüne dayanarak, işte güncellenmiş bir çözüm:

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

http://example.com


Tırnak işaretleri JSON dizesini bozabileceğinden ve JSON ayrıştırma hatalarına neden olabileceğinden, bu bazı dizeler için çalışmaz. Bu durumlarda diğer yanıtı ( stackoverflow.com/a/7885499/249327 ) kullandım .
nickdos

2

Bunu yorumların altına mevcut cevaplara koymak için yeterli temsilcim yok:

unescapeyalnızca URI'larla (veya kodlanmış herhangi bir utf-8) çalıştığı için kullanımdan kaldırılmıştır ki bu muhtemelen çoğu insanın ihtiyaçları için geçerlidir. encodeURIComponentjs dizesini çıkış karakterli UTF-8'e dönüştürür ve decodeURIComponentyalnızca çıkış karakterli UTF-8 baytlarında çalışır. decodeURIComponent('%a9'); // errorUzatılmış ascii geçerli utf-8 olmadığı için bir hata atar (bu hala bir unicode değeri olmasına rağmen), oysa unescape('%a9'); // ©decodeURIComponent kullanırken verilerinizi bilmeniz gerekir.

decodeURIComponent, utf-8'de bir vekilin bir parçasını gösterdiği için üzerinde "%C2"veya herhangi bir tek bayt üzerinde çalışmaz 0x7f. Bununla birlikte, decodeURIComponent("%C2%A9") //gives you ©Unescape bu konuda düzgün çalışmaz // ©VE bir hata vermez, bu nedenle verilerinizi bilmiyorsanız unescape hatalı koda yol açabilir.


1

Bunun için kullanmak JSON.decode, bilmeniz gereken önemli dezavantajlarla birlikte gelir:

  • Dizeyi çift tırnak içine almalısınız
  • Birçok karakter desteklenmez ve kendilerinin kaçması gerekir. Örneğin, için aşağıdakilerden herhangi geçen JSON.decode(çift tırnak onları tamamlamasından sonra) bunların hepsi geçerli olsa bile hata olacaktır: \\n, \n, \\0,a"a
  • Onaltılık çıkışları desteklemez: \\x45
  • Unicode kod noktası dizilerini desteklemez: \\u{045}

Başka uyarılar da var. Esasen, JSON.decodebu amaçla kullanmak bir hack'tir ve her zaman beklediğiniz gibi çalışmaz. JSONKütüphaneyi dize işlemlerini değil, JSON'u işlemek için kullanmaya devam etmelisiniz .


Geçenlerde bu konuyla kendim karşılaştım ve sağlam bir kod çözücü istedim, bu yüzden kendim bir tane yazdım. Eksiksiz ve kapsamlı bir şekilde test edilmiştir ve burada mevcuttur: https://github.com/iansan5653/unraw . JavaScript standardını olabildiğince yakından taklit eder.

Açıklama:

Kaynak yaklaşık 250 satırdır, bu yüzden hepsini buraya dahil etmeyeceğim, ancak esasen tüm kaçış dizilerini bulmak için aşağıdaki Regex'i kullanır ve ardından bunları parseInt(string, 16)16 tabanlı sayıların kodunu çözmek ve ardından String.fromCodePoint(number)karşılık gelen karakteri elde etmek için ayrıştırır :

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

Yorumlandı (NOT: Bu normal ifade, geçersiz olanlar da dahil olmak üzere tüm kaçış dizileriyle eşleşir. Dize, JS'de bir hata atarsa, kitaplığımda bir hata atar [yani, '\x!!'hata verir]):

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

Misal

Bu kitaplığı kullanarak:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.