Como decodifico uma string com Unicode de escape?

92

Não tenho certeza de como é chamado, então estou tendo problemas para pesquisar. Como posso decodificar uma string com Unicode de http\u00253A\u00252F\u00252Fexample.compara http://example.comcom JavaScript? Eu tentei unescape, decodeURIe decodeURIComponententão acho que a única coisa que resta é a substituição da corda.

EDIT: A string não é digitada, mas sim uma substring de outro trecho de código. Portanto, para resolver o problema, você deve começar com algo assim:

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

Espero que isso mostre por que unescape () não funciona.

estilo
fonte
De onde vem a corda?
Cameron,
@Cameron: A string vem de um script que chamei de innerHTML para obter. É por isso que a resposta de alex não funciona.
styfle

Respostas:

113

Editar (12-10-2017) :

@MechaLynx e @ Kevin-Weber observam que unescape()foi descontinuado em ambientes sem navegador e não existe no TypeScript. decodeURIComponenté um substituto imediato. Para uma compatibilidade mais ampla, use o seguinte:

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

Resposta original:

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

Você pode descarregar todo o trabalho para JSON.parse

Radicand
fonte
7
Interessante. Tive de adicionar aspas. unescape(JSON.parse('"' + s + '"'));Qual é a razão das citações extras? Isso o torna um JSON válido?
estiloso
1
Observe que isso parece ser significativamente mais rápido do que a fromCharCodeabordagem: jsperf.com/unicode-func-vs-json-parse
nrabinowitz
17
Observação importante sobre a resposta de @styfle: não use JSON.parse('"' + s + '"')ao lidar com dados não confiáveis JSON.parse('"' + s.replace('"', '\\"') + '"'), caso contrário, seu código será interrompido quando a entrada contiver aspas.
ntninja
7
Ótima resposta @ alexander255, mas você realmente gostaria de usar: JSON.parse ('"' + str.replace (/ \" / g, '\\ "' + '"') para substituir TODAS as ocorrências desse caractere em todo o string, em vez de substituir uma.
CS
2
Para quem se depara com isso e está preocupado porque unescape()foi descontinuado, decodeURIComponent()funciona de forma idêntica unescape()neste caso, então apenas substitua por isso e está tudo bem.
mechalynx
116

ATUALIZAÇÃO : Por favor, note que esta é uma solução que deve ser aplicada a navegadores mais antigos ou plataformas sem navegadores e é mantida ativa para fins de instrução. Consulte a resposta de @radicand abaixo para obter uma resposta mais atualizada.


Esta é uma string unicode com escape. Primeiro, a string foi escapada e, em seguida, codificada com Unicode. Para converter de volta ao 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

Para explicar: eu uso uma expressão regular para procurar \u0025. No entanto, como preciso apenas de uma parte dessa string para minha operação de substituição, uso parênteses para isolar a parte que vou reutilizar 0025,. Essa parte isolada é chamada de grupo.

A giparte no final da expressão indica que ela deve corresponder a todas as instâncias da string, não apenas à primeira, e que a correspondência deve ser insensível a maiúsculas e minúsculas. Isso pode parecer desnecessário dado o exemplo, mas adiciona versatilidade.

Agora, para converter de uma string para a próxima, preciso executar algumas etapas em cada grupo de cada correspondência, e não posso fazer isso simplesmente transformando a string. Felizmente, a operação String.replace pode aceitar uma função, que será executada para cada correspondência. O retorno dessa função substituirá a própria correspondência na string.

Eu uso o segundo parâmetro que esta função aceita, que é o grupo que preciso usar, e transformo-o na sequência utf-8 equivalente e, em seguida, uso a unescapefunção interna para decodificar a string em sua forma adequada.

Ioannis Karadimas
fonte
3
Obrigado. Você poderia explicar um pouco sobre o que está fazendo? Parece que a regex está procurando um \uprefixo e não um número hexadecimal de 4 caracteres (letras ou números). Como funciona a função no método de substituição?
Styfle
1
Você está certo, isso precisava de uma explicação, então eu atualizei meu post. Apreciar!
Ioannis Karadimas
1
Ótima solução. No meu caso, estou codificando todos os caracteres internacionais (não ascii) enviados do servidor como unicode com escape e, em seguida, usando sua função no navegador para decodificar os caracteres para os caracteres UTF-8 corretos. Descobri que precisava atualizar o seguinte regex para capturar caracteres de todos os idiomas (ou seja, tailandês):var r = /\\u([\d\w]{1,})/gi;
Nathan Hanna
2
Observe que isso parece ser significativamente mais lento do que a JSON.parseabordagem: jsperf.com/unicode-func-vs-json-parse
nrabinowitz
1
@IoannisKaradimas Certamente existe uma coisa chamada depreciação em Javascript. Reivindicar isso e, em seguida, apoiá-lo afirmando que os navegadores mais antigos devem sempre ser suportados é uma perspectiva completamente a-histórica. Em qualquer caso, quem quiser usar isso e também quiser evitar, unescape()pode usar decodeURIComponent(). Funciona de forma idêntica neste caso. Eu recomendaria a abordagem de radicand no entanto, por ser mais simples, tão suportada e mais rápida de executar, com os mesmos resultados (certifique-se de ler os comentários, no entanto).
mechalynx
21

Observe que o uso de unescape()está obsoleto e não funciona com o compilador TypeScript, por exemplo.

Com base na resposta de Radicand e na seção de comentários abaixo, aqui está uma solução atualizada:

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

http://example.com

Kevin weber
fonte
Isso não funciona para algumas strings, pois as aspas podem quebrar a string JSON e resultar em erros de análise JSON. Usei a outra resposta ( stackoverflow.com/a/7885499/249327 ) nesses casos.
nickdos
2

Não tenho representante suficiente para colocar isso em comentários às respostas existentes:

unescapesó está obsoleto para trabalhar com URIs (ou qualquer utf-8 codificado), o que provavelmente é o caso para as necessidades da maioria das pessoas. encodeURIComponentconverte uma string js em UTF-8 de escape e decodeURIComponentsó funciona em bytes UTF-8 de escape. Ele lança um erro para algo como decodeURIComponent('%a9'); // errorporque ascii estendido não é utf-8 válido (mesmo que ainda seja um valor Unicode), enquanto unescape('%a9'); // ©So você precisa saber seus dados ao usar decodeURIComponent.

decodeURIComponent não funcionará em "%C2"nenhum byte solitário 0x7fporque em utf-8 isso indica parte de um substituto. No entanto, o decodeURIComponent("%C2%A9") //gives you ©Unescape não funcionaria corretamente nisso // ©E não geraria um erro, então o unescape pode levar a um código com erros se você não souber seus dados.

aamarks
fonte
1

Usar JSON.decodepara isso tem desvantagens significativas, das quais você deve estar ciente:

  • Você deve envolver a string entre aspas duplas
  • Muitos caracteres não são suportados e devem ser escapados. Por exemplo, passar qualquer um dos seguintes para JSON.decode(depois de envolvê-los em aspas) irá erro mesmo que estes são todos válidos: \\n, \n, \\0,a"a
  • Não suporta escapes hexadecimais: \\x45
  • Não suporta sequências de pontos de código Unicode: \\u{045}

Existem outras advertências também. Essencialmente, usar JSON.decodepara esse propósito é um hack e não funciona da maneira que você sempre esperava. Você deve usar a JSONbiblioteca para lidar com JSON, não para operações de string.


Recentemente, eu mesmo tive esse problema e queria um decodificador robusto, então acabei escrevendo um sozinho. Está completo e exaustivamente testado e está disponível aqui: https://github.com/iansan5653/unraw . Ele imita o padrão JavaScript o mais próximo possível.

Explicação:

A fonte tem cerca de 250 linhas, então não vou incluir tudo aqui, mas basicamente ele usa o seguinte Regex para encontrar todas as sequências de escape e depois as analisa usando parseInt(string, 16)para decodificar os números de base 16 e String.fromCodePoint(number)obter o caractere correspondente:

/\\(?:(\\)|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 (OBSERVAÇÃO: esta regex corresponde a todas as sequências de escape, incluindo as inválidas. Se a string gerasse um erro em JS, ela geraria um erro em minha biblioteca [ou seja, '\x!!'erro de erro]):

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

Exemplo

Usando essa 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
fonte