Come si decodifica una stringa con Unicode con escape?


89

Non sono sicuro di come si chiami, quindi ho problemi a cercarlo. Come posso decodificare una stringa con unicode da http\u00253A\u00252F\u00252Fexample.coma http://example.comcon JavaScript? Ho provato unescape, decodeURIe decodeURIComponentquindi immagino che l'unica cosa rimasta sia la sostituzione della stringa.

EDIT: la stringa non è digitata, ma piuttosto una sottostringa da un altro pezzo di codice. Quindi per risolvere il problema devi iniziare con qualcosa del genere:

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

Spero che questo dimostri perché unescape () non funziona.


Da dove viene la corda?
Cameron

@Cameron: la stringa proviene da uno script che ho chiamato innerHTML per ottenere. Questo è il motivo per cui la risposta di Alex non funziona.
stile

Risposte:


109

Modifica (12/10/2017) :

@MechaLynx e @ Kevin-Weber notano che unescape()è deprecato da ambienti non browser e non esiste in TypeScript. decodeURIComponentè un sostituto immediato. Per una compatibilità più ampia, utilizza invece quanto segue:

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

Risposta originale:

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

Puoi scaricare tutto il lavoro su JSON.parse


6
Interessante. Ho dovuto aggiungere virgolette intorno ad esso unescape(JSON.parse('"' + s + '"'));Qual è il motivo per le virgolette extra? Questo lo rende JSON valido?
stile

1
Nota che questo sembra essere significativamente più veloce fromCharCodedell'approccio: jsperf.com/unicode-func-vs-json-parse
nrabinowitz

17
Nota importante sulla risposta di @styfle: non utilizzare invece JSON.parse('"' + s + '"')quando si tratta di dati non attendibili JSON.parse('"' + s.replace('"', '\\"') + '"'), altrimenti il ​​codice si interromperà quando l'input contiene virgolette.
ntninja

7
Ottima risposta @ alexander255, ma in realtà vorresti usare: JSON.parse ('"' + str.replace (/ \" / g, '\\ "' + '"') per sostituire TUTTE le occorrenze di quel carattere in tutto il stringa, anziché sostituirne una.
CS

2
Per coloro che si imbattono in questo e sono preoccupati perché unescape()è stato deprecato, decodeURIComponent()funziona in modo identico unescape()in questo caso, quindi sostituiscilo con quello e sei a posto.
mechalynx

116

AGGIORNAMENTO : si prega di notare che questa è una soluzione che dovrebbe essere applicata ai browser meno recenti o alle piattaforme non browser ed è mantenuta in vita per scopi didattici. Si prega di fare riferimento alla risposta di @radicand di seguito per una risposta più aggiornata.


Questa è una stringa unicode con escape. Prima la stringa è stata sottoposta a escape, quindi codificata con Unicode. Per tornare alla normalità:

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

Per spiegare: utilizzo un'espressione regolare per cercare \u0025. Tuttavia, dal momento che ho bisogno di solo una parte di questa stringa per la mia operazione di sostituzione, io uso le parentesi per isolare la parte ho intenzione di riutilizzo, 0025. Questa parte isolata è chiamata gruppo.

La giparte alla fine dell'espressione indica che dovrebbe corrispondere a tutte le istanze nella stringa, non solo alla prima, e che la corrispondenza dovrebbe essere senza distinzione tra maiuscole e minuscole. Questo potrebbe sembrare inutile dato l'esempio, ma aggiunge versatilità.

Ora, per convertire da una stringa all'altra, devo eseguire alcuni passaggi su ciascun gruppo di ogni corrispondenza e non posso farlo semplicemente trasformando la stringa. Utilmente, l'operazione String.replace può accettare una funzione, che verrà eseguita per ogni corrispondenza. Il ritorno di quella funzione sostituirà la corrispondenza stessa nella stringa.

Uso il secondo parametro che questa funzione accetta, che è il gruppo che devo usare, e lo trasformo nell'equivalente sequenza utf-8, quindi uso la unescapefunzione incorporata per decodificare la stringa nella sua forma corretta.


3
Grazie. Potresti spiegare un po 'quello che stai facendo? Sembra che la regex stia cercando un \uprefisso e un numero esadecimale di 4 caratteri (lettere o numeri). Come funziona la funzione nel metodo di sostituzione?
stile

1
Hai ragione, aveva bisogno di una spiegazione, quindi ho aggiornato il mio post. Godere!
Ioannis Karadimas

1
Ottima soluzione. Nel mio caso, sto codificando tutti i caratteri internazionali (non ASCII) inviati dal server come unicode con escape, quindi utilizzo la tua funzione nel browser per decodificare i caratteri nei caratteri UTF-8 corretti. Ho scoperto che dovevo aggiornare la seguente regex per catturare i caratteri di tutte le lingue (es. Thailandese):var r = /\\u([\d\w]{1,})/gi;
Nathan Hanna

2
Nota che questo sembra essere significativamente più lento JSON.parsedell'approccio: jsperf.com/unicode-func-vs-json-parse
nrabinowitz

1
@IoannisKaradimas C'è sicuramente qualcosa come deprecazione in Javascript. Affermarlo e poi supportarlo affermando che i browser più vecchi devono essere sempre supportati è una prospettiva completamente astorica. In ogni caso, chiunque voglia usarlo e voglia anche evitarlo unescape()può decodeURIComponent()invece usarlo . Funziona in modo identico in questo caso. Tuttavia, consiglierei l'approccio di radicand, poiché è più semplice, altrettanto supportato e più veloce da eseguire, con gli stessi risultati (assicurati di leggere i commenti).
mechalynx

21

Tieni presente che l'uso di unescape()è deprecato e non funziona con il compilatore TypeScript, ad esempio.

Sulla base della risposta di radicand e della sezione commenti qui sotto, ecco una soluzione aggiornata:

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

http://example.com


Questo non funziona per alcune stringhe, poiché le virgolette possono rompere la stringa JSON e causare errori di analisi JSON. Ho usato l'altra risposta ( stackoverflow.com/a/7885499/249327 ) in questi casi.
nickdos

2

Non ho abbastanza rappresentante per inserirlo nei commenti alle risposte esistenti:

unescapeè deprecato solo per lavorare con gli URI (o qualsiasi utf-8 codificato) che è probabilmente il caso delle esigenze della maggior parte delle persone. encodeURIComponentconverte una stringa js in UTF-8 con escape e decodeURIComponentfunziona solo su byte UTF-8 con escape. Genera un errore per qualcosa come decodeURIComponent('%a9'); // errorperché ascii esteso non è valido utf-8 (anche se è ancora un valore unicode), mentre unescape('%a9'); // ©Quindi devi conoscere i tuoi dati quando usi decodeURIComponent.

decodeURIComponent non funzionerà "%C2"o non funzionerà su nessun byte solitario 0x7fperché in utf-8 ciò indica parte di un surrogato. Tuttavia decodeURIComponent("%C2%A9") //gives you ©Unescape non funzionerebbe correttamente su quello // ©E non genererebbe un errore, quindi unescape può portare a codice difettoso se non conosci i tuoi dati.


1

L'utilizzo JSON.decodeper questo comporta svantaggi significativi di cui devi essere a conoscenza:

  • È necessario racchiudere la stringa tra virgolette doppie
  • Molti caratteri non sono supportati e devono essere salvati da soli. Ad esempio, passando una delle seguenti a JSON.decode(dopo avvolgendoli in doppi apici) sarà errore anche se questi sono tutti validi: \\n, \n, \\0,a"a
  • Non supporta gli escape esadecimali: \\x45
  • Non supporta sequenze di punti di codice Unicode: \\u{045}

Ci sono anche altri avvertimenti. In sostanza, l'utilizzo JSON.decodeper questo scopo è un trucco e non funziona nel modo in cui potresti sempre aspettarti. Dovresti continuare a usare la JSONlibreria per gestire JSON, non per le operazioni sulle stringhe.


Di recente mi sono imbattuto in questo problema e volevo un decoder robusto, quindi ho finito per scriverne uno io stesso. È completo e accuratamente testato ed è disponibile qui: https://github.com/iansan5653/unraw . Imita lo standard JavaScript il più fedelmente possibile.

Spiegazione:

La fonte è di circa 250 righe, quindi non includerò tutto qui, ma essenzialmente utilizza il seguente Regex per trovare tutte le sequenze di escape e quindi le analizza usando parseInt(string, 16)per decodificare i numeri in base 16 e quindi String.fromCodePoint(number)per ottenere il carattere corrispondente:

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

Commentato (NOTA: questa regex corrisponde a tutte le sequenze di escape, comprese quelle non valide. Se la stringa genera un errore in JS, genera un errore nella mia libreria [cioè, '\x!!'errore]):

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

Esempio

Utilizzando quella libreria:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.