¿Cómo decodifico una cadena con unicode de escape?

89

No estoy seguro de cómo se llama, así que tengo problemas para buscarlo. ¿Cómo puedo decodificar una cadena con unicode de http\u00253A\u00252F\u00252Fexample.coma http://example.comcon JavaScript? Lo intenté unescape, decodeURIy decodeURIComponentsupongo que lo único que queda es reemplazar la cadena.

EDITAR: La cadena no se escribe, sino una subcadena de otra pieza de código. Entonces, para resolver el problema, debes comenzar con algo como esto:

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

Espero que eso demuestre por qué unescape () no funciona.

styfle
fuente
¿De dónde viene la cuerda?
Cameron
@Cameron: La cadena es de un script que llamé innerHTML para obtener. Es por eso que la respuesta de Alex no funciona.
styfle

Respuestas:

109

Editar (12/10/2017) :

@MechaLynx y @ Kevin-Weber notan que unescape()está obsoleto en entornos que no son de navegador y no existe en TypeScript. decodeURIComponentes un reemplazo directo. Para una compatibilidad más amplia, use lo siguiente en su lugar:

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

Respuesta original:

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

Puedes descargar todo el trabajo a JSON.parse

radicando
fuente
6
Interesante. Tuve que agregar comillas a su alrededor. unescape(JSON.parse('"' + s + '"'));¿Cuál es el motivo de las comillas adicionales? ¿Eso lo convierte en JSON válido?
styfle
1
Tenga en cuenta que esto parece ser significativamente más rápido que el fromCharCodeenfoque: jsperf.com/unicode-func-vs-json-parse
nrabinowitz
17
Nota importante sobre la respuesta de @ styfle: no lo use JSON.parse('"' + s + '"')cuando se trate de datos no confiables JSON.parse('"' + s.replace('"', '\\"') + '"'), de lo contrario, su código se romperá cuando la entrada contenga comillas.
ntninja
7
Excelente respuesta @ alexander255, pero en realidad le gustaría usar: JSON.parse ('"' + str.replace (/ \" / g, '\\ "' + '"') para reemplazar TODAS las apariciones de ese carácter en todo el cadena, en lugar de reemplazar una.
CS
2
Para aquellos que se encuentran con esto y están preocupados porque unescape()se ha desaprobado, decodeURIComponent()funciona de manera idéntica unescape()en este caso, así que simplemente reemplácelo con eso y estará bien.
mechalynx
116

ACTUALIZACIÓN : tenga en cuenta que esta es una solución que debe aplicarse a navegadores más antiguos o plataformas que no son navegadores, y se mantiene activa con fines educativos. Consulte la respuesta de @radicand a continuación para obtener una respuesta más actualizada.


Esta es una cadena de escape Unicode. Primero se escapó la cadena, luego se codificó con Unicode. Para volver a la normalidad:

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

Para explicar: utilizo una expresión regular para buscar \u0025. Sin embargo, ya que necesito sólo una parte de esta cadena para mi operación de reemplazo, uso de paréntesis para aislar la parte que voy a reutilización, 0025. Esta parte aislada se llama grupo.

La giparte al final de la expresión indica que debe coincidir con todas las instancias de la cadena, no solo con la primera, y que la coincidencia no debe distinguir entre mayúsculas y minúsculas. Esto puede parecer innecesario dado el ejemplo, pero agrega versatilidad.

Ahora, para convertir de una cadena a la siguiente, necesito ejecutar algunos pasos en cada grupo de cada coincidencia, y no puedo hacerlo simplemente transformando la cadena. Afortunadamente, la operación String.replace puede aceptar una función, que se ejecutará para cada coincidencia. El retorno de esa función reemplazará la coincidencia en la cadena.

Utilizo el segundo parámetro que acepta esta función, que es el grupo que necesito usar, y lo transformo a la secuencia utf-8 equivalente, luego uso la unescapefunción incorporada para decodificar la cadena a su forma adecuada.

Ioannis Karadimas
fuente
3
Gracias. ¿Podría explicarnos un poco lo que está haciendo? Parece que la expresión regular busca un \uprefijo y un número hexadecimal de 4 caracteres (letras o números). ¿Cómo funciona la función en el método de reemplazo?
styfle
1
Tienes razón, eso necesitaba una explicación, así que actualicé mi publicación. ¡Disfrutar!
Ioannis Karadimas
1
Gran solucion En mi caso, estoy codificando todos los caracteres internacionales (no ascii) que se envían desde el servidor como unicode de escape, luego uso su función en el navegador para decodificar los caracteres a los caracteres UTF-8 correctos. Descubrí que tenía que actualizar la siguiente expresión regular para capturar caracteres de todos los idiomas (es decir, tailandés):var r = /\\u([\d\w]{1,})/gi;
Nathan Hanna
2
Tenga en cuenta que esto parece ser significativamente más lento que el JSON.parseenfoque: jsperf.com/unicode-func-vs-json-parse
nrabinowitz
1
@IoannisKaradimas Sin duda alguna, Javascript está en desuso. Afirmar eso y luego respaldarlo afirmando que los navegadores más antiguos siempre deben ser compatibles es una perspectiva completamente ahistórica. En cualquier caso, cualquiera que quiera usar esto y también quiera evitarlo unescape()puede usarlo decodeURIComponent(). Funciona de forma idéntica en este caso. Sin embargo, recomendaría el enfoque de radicand, ya que es más simple, igual de compatible y más rápido de ejecutar, con los mismos resultados (asegúrese de leer los comentarios sin embargo).
mechalynx
21

Tenga en cuenta que el uso de unescape()está obsoleto y no funciona con el compilador de TypeScript, por ejemplo.

Según la respuesta de radicand y la sección de comentarios a continuación, aquí hay una solución actualizada:

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

http://example.com

Kevin Weber
fuente
Esto no funciona para algunas cadenas, ya que las comillas pueden romper la cadena JSON y dar como resultado errores de análisis JSON. Usé la otra respuesta ( stackoverflow.com/a/7885499/249327 ) en estos casos.
nickdos
2

No tengo suficiente representante para poner esto en comentarios a las respuestas existentes:

unescapesolo está en desuso para trabajar con URI (o cualquier utf-8 codificado) que es probablemente el caso para las necesidades de la mayoría de las personas. encodeURIComponentconvierte una cadena js en UTF-8 de escape y decodeURIComponentsolo funciona en bytes UTF-8 de escape. Lanza un error para algo como decodeURIComponent('%a9'); // errorporque ascii extendido no es utf-8 válido (aunque sigue siendo un valor unicode), mientras que, unescape('%a9'); // ©por lo tanto, necesita conocer sus datos cuando use decodeURIComponent.

decodeURIComponent no funcionará en "%C2"ningún byte solitario 0x7fporque en utf-8 eso indica parte de un sustituto. Sin embargo, decodeURIComponent("%C2%A9") //gives you ©Unescape no funcionaría correctamente en eso // ©Y no arrojaría un error, por lo que unescape puede generar un código defectuoso si no conoce sus datos.

aamarks
fuente
1

El uso JSON.decodepara esto conlleva importantes inconvenientes que debe tener en cuenta:

  • Debes envolver la cadena entre comillas dobles.
  • Muchos personajes no son compatibles y deben escaparse ellos mismos. Por ejemplo, pasar algo de lo siguiente para JSON.decode(después de concluir entre comillas dobles) será de error aunque éstas son todas válidas: \\n, \n, \\0,a"a
  • No admite escapes hexadecimales: \\x45
  • No admite secuencias de puntos de código Unicode: \\u{045}

También hay otras advertencias. Esencialmente, usarlo JSON.decodepara este propósito es un truco y no funciona de la manera que siempre podría esperar. Debe seguir usando la JSONbiblioteca para manejar JSON, no para operaciones de cadena.


Recientemente me encontré con este problema y quería un decodificador robusto, así que terminé escribiendo uno yo mismo. Está completo y probado a fondo y está disponible aquí: https://github.com/iansan5653/unraw . Imita el estándar JavaScript lo más fielmente posible.

Explicación:

La fuente es de aproximadamente 250 líneas, por lo que no lo incluiré todo aquí, pero esencialmente usa la siguiente expresión regular para encontrar todas las secuencias de escape y luego las analiza parseInt(string, 16)para decodificar los números en base 16 y luego String.fromCodePoint(number)para obtener el carácter correspondiente:

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

Comentado (NOTA: Esta expresión regular coincide con todas las secuencias de escape, incluidas las no válidas. Si la cadena arrojaría un error en JS, arrojaría un error en mi biblioteca [es decir, '\x!!'generará un error]):

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

Ejemplo

Usando esa biblioteca:

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
fuente