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

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

EDIT: строка не напечатана, а подстрока из другого фрагмента кода. Поэтому, чтобы решить проблему, вы должны начать с чего-то вроде этого:

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

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

Ответ 1

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

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

Вы можете разгрузить всю работу до JSON.parse

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

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

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

Ответ 2

ОБНОВЛЕНИЕ. Обратите внимание, что это решение, которое должно применяться к старым браузерам или платформам, отличным от браузера, и поддерживается в целях обучения. Пожалуйста, обратитесь к @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

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

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

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

http://example.com

Ответ 5

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

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 может привести к ошибкам, если вы не знаете свои данные.

Ответ 6

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

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

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


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

Объяснение:

Исходный текст содержит около 250 строк, поэтому я не буду здесь все включать, но, по сути, он использует следующее регулярное выражение для поиска всех escape-последовательностей, а затем анализирует их, используя parseInt(string, 16) для декодирования чисел base-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 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 ('.' does not match newlines)
| # or
$ # Match the end of the string
) # End the group of 'or' statements
/g # Match as many instances as there are
Пример

Example

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

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