Как мне декодировать строку с помощью экранированного юникода?

89

Я не знаю, как это называется, поэтому у меня проблемы с поиском. Как я могу декодировать строку с помощью Unicode от http\u00253A\u00252F\u00252Fexample.comдо http://example.comс помощью JavaScript? Я пробовал unescape, decodeURIи decodeURIComponentпоэтому думаю, что осталось только заменить строку.

РЕДАКТИРОВАТЬ: строка не вводится, а является подстрокой из другого фрагмента кода. Итак, чтобы решить проблему, вам нужно начать с чего-то вроде этого:

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

Надеюсь, это показывает, почему unescape () не работает.

стиль
источник
Откуда струна?
Кэмерон
@Cameron: Строка взята из сценария, который я вызвал innerHTML для получения. Вот почему ответ Алекс не работает.
Styfle

Ответы:

109

Изменить (2017-10-12) :

@MechaLynx и @ Kevin-Weber отмечают, что unescape()это не рекомендуется в средах, отличных от браузеров, и не существует в TypeScript. decodeURIComponentэто прямая замена. Для более широкой совместимости используйте вместо этого следующее:

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

Оригинальный ответ:

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

Вы можете переложить всю работу на JSON.parse

прикорневой
источник
6
Интересно. Мне пришлось добавить кавычки вокруг него. В unescape(JSON.parse('"' + s + '"'));чем причина лишних кавычек? Это делает его действительным JSON?
Styfle
1
Обратите внимание, что это кажется значительно быстрее, чем fromCharCodeподход: jsperf.com/unicode-func-vs-json-parse
nrabinowitz
17
Важное примечание об ответе @styfle: не используйте вместо этого JSON.parse('"' + s + '"')при работе с ненадежными данными JSON.parse('"' + s.replace('"', '\\"') + '"'), иначе ваш код сломается, когда ввод содержит кавычки.
ntninja
7
Отличный ответ @ alexander255, но вы действительно хотели бы использовать: JSON.parse ('"' + str.replace (/ \" / g, '\\ "' + '"'), чтобы заменить ВСЕ вхождения этого символа во всем строка, а не заменять одну.
CS
2
Для тех, кто сталкивается с этим и обеспокоен тем, что unescape()он устарел, decodeURIComponent()работает так же, как unescape()в этом случае, поэтому просто замените его этим, и все будет хорошо.
mechalynx
116

ОБНОВЛЕНИЕ : обратите внимание, что это решение, которое должно применяться к более старым браузерам или небраузерным платформам, и поддерживается в учебных целях. Пожалуйста, обратитесь к ответу @radicand ниже, чтобы получить более свежий ответ.


Это экранированная строка в кодировке Unicode. Сначала строка была экранирована, а затем закодирована с помощью Unicode. Чтобы вернуться к нормальному состоянию:

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

Чтобы объяснить: я ищу регулярное выражение \u0025. Однако, так как мне нужно только часть этой строки для моей замены операции, я использую круглые скобки , чтобы изолировать часть я собираюсь повторного использования 0025. Эта изолированная часть называется группой.

giЧасть в конце выражения обозначает оно должно соответствовать всем экземплярам в строке, а не только первый из них, и что согласование должно быть чувствительно к регистру. Это может показаться ненужным на примере, но добавляет универсальности.

Теперь, чтобы преобразовать одну строку в другую, мне нужно выполнить несколько шагов для каждой группы каждого совпадения, и я не могу сделать это, просто преобразовав строку. К счастью, операция String.replace может принимать функцию, которая будет выполняться для каждого совпадения. Возврат этой функции заменит само совпадение в строке.

Я использую второй параметр, который принимает эта функция, то есть группу, которую мне нужно использовать, и преобразовываю его в эквивалентную последовательность utf-8, а затем использую встроенную unescapeфункцию для декодирования строки в ее правильную форму.

Иоаннис Карадимас
источник
3
Спасибо. Не могли бы вы немного объяснить, что вы делаете? Похоже, что регулярное выражение ищет \uпрефикс, а не четырехзначное шестнадцатеричное число (буквы или цифры). Как работает функция в методе замены?
Styfle
1
Вы правы, это потребовало объяснений, поэтому я обновил свой пост. Наслаждайтесь!
Иоаннис Карадимас
1
Отличное решение. В моем случае я кодирую все международные (не-ascii) символы, отправляемые с сервера, как экранированные символы Unicode, а затем использую вашу функцию в браузере для декодирования символов в правильные символы UTF-8. Я обнаружил, что мне пришлось обновить следующее регулярное выражение, чтобы улавливать символы со всех языков (например, тайского):var r = /\\u([\d\w]{1,})/gi;
Nathan Hanna
2
Обратите внимание, что это, похоже, значительно медленнее, чем JSON.parseподход: jsperf.com/unicode-func-vs-json-parse
nrabinowitz
1
@IoannisKaradimas В Javascript наверняка есть такая вещь, как отказ от поддержки. Утверждать это, а затем поддерживать это, заявляя, что старые браузеры всегда должны поддерживаться, - это совершенно антиисторическая точка зрения. В любом случае, любой, кто хочет использовать это, а также хочет избежать, unescape()может использовать decodeURIComponent()вместо этого. В этом случае работает идентично. Однако я бы рекомендовал подход Radicand, поскольку он проще, так же поддерживается и быстрее выполняется с теми же результатами (однако обязательно прочтите комментарии).
mechalynx
21

Обратите внимание, что использование unescape()не рекомендуется и, например, не работает с компилятором TypeScript.

Основываясь на ответе подкоренного выражения и разделе комментариев ниже, вот обновленное решение:

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

http://example.com

Кевин Вебер
источник
Это не работает для некоторых строк, поскольку кавычки могут нарушить строку JSON и привести к ошибкам анализа JSON. В этих случаях я использовал другой ответ ( stackoverflow.com/a/7885499/249327 ).
nickdos 04
2

У меня недостаточно репутации, чтобы поместить это в комментарии к существующим ответам:

unescapeустарел только для работы с URI (или любым закодированным utf-8), что, вероятно, подходит для нужд большинства людей. encodeURIComponentпреобразует строку js в экранированный UTF-8 и decodeURIComponentработает только с экранированными байтами UTF-8. Он выдает ошибку, например, decodeURIComponent('%a9'); // errorпотому что расширенный ascii недействителен utf-8 (хотя это все еще значение Unicode), тогда как unescape('%a9'); // ©вам нужно знать свои данные при использовании decodeURIComponent.

decodeURIComponent не будет работать "%C2"ни с одним единственным байтом, 0x7fпотому что в utf-8 указывается часть суррогата. Однако decodeURIComponent("%C2%A9") //gives you ©Unescape не будет работать с этим должным образом, // Â©И это не приведет к возникновению ошибки, поэтому unescape может привести к ошибочному коду, если вы не знаете свои данные.

Aamarks
источник
1

Использование JSON.decodeдля этого имеет существенные недостатки, о которых вы должны знать:

  • Вы должны заключить строку в двойные кавычки
  • Многие символы не поддерживаются, и их нужно экранировать. Например, передача любого из следующих способов JSON.decode(после упаковки их в двойных кавычках) будет ошибка , даже если они являются корректными: \\n, \n, \\0,a"a
  • Он не поддерживает шестнадцатеричные escape-последовательности: \\x45
  • Он не поддерживает последовательности кодовых точек Unicode: \\u{045}

Есть и другие предостережения. По сути, использование JSON.decodeдля этой цели - это хитрость и не работает так, как вы всегда ожидали. Вам следует использовать JSONбиблиотеку для обработки JSON, а не для строковых операций.


Недавно я сам столкнулся с этой проблемой и хотел получить надежный декодер, поэтому в итоге я написал его сам. Он полностью и тщательно протестирован и доступен здесь: https://github.com/iansan5653/unraw . Он максимально приближен к стандарту JavaScript.

Пояснение:

Источник составляет около 250 строк, поэтому я не буду включать его здесь все, но по сути он использует следующее регулярное выражение для поиска всех escape-последовательностей, а затем анализирует их, используя parseInt(string, 16)для декодирования чисел с основанием 16, а затем String.fromCodePoint(number)для получения соответствующего символа:

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

Прокомментировано (ПРИМЕЧАНИЕ. Это регулярное выражение соответствует всем escape-последовательностям, включая недопустимые. Если строка выдает ошибку в JS, она вызывает ошибку в моей библиотеке [то есть, '\x!!'будет ошибка]):

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

пример

Используя эту библиотеку:

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
Ян
источник