Як декодувати рядок із захищеним Unicode?


89

Я не впевнений, як це називається, тому у мене виникають проблеми з його пошуком. Як я можу декодувати рядок за допомогою Unicode від http\u00253A\u00252F\u00252Fexample.comдо за http://example.comдопомогою JavaScript? Я спробував unescape, decodeURIі decodeURIComponentтому, мабуть, єдине, що залишилось - це заміна рядка.

РЕДАГУВАТИ: рядок не набирається, а скоріше підрядок з іншого фрагмента коду. Отже, щоб вирішити проблему, потрібно почати з приблизно такого:

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

Сподіваюся, це показує, чому unescape () не працює.


Звідки береться струна?
Cameron

@Cameron: Рядок зі сценарію, який я назвав innerHTML, щоб отримати. Ось чому відповідь Алекса не працює.
styfle

Відповіді:


109

Редагувати (12.10.2017) :

@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. Щоб перетворити назад у звичайне:

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префікс, а не 4-символьний шістнадцятковий номер (літери або цифри). Як працює функція в методі replace?
styfle

1
Ви маєте рацію, що потребували пояснень, тому я оновив свій пост. Насолоджуйтесь!
Йоанніс Карадімас

1
Чудове рішення. У моєму випадку я кодую всі міжнародні (не-ascii) символи, що надсилаються з сервера як захищений юнікод, а потім використовую вашу функцію в браузері для декодування символів до правильних символів UTF-8. Я виявив, що мені довелося оновити наступний регулярний вираз, щоб ловити символи з усіх мов (тобто тайської):var r = /\\u([\d\w]{1,})/gi;
Натан Ханна,

2
Зверніть увагу, що це, здається, значно повільніше, ніж JSON.parseпідхід: jsperf.com/unicode-func-vs-json-parse
nrabinowitz

1
@IoannisKaradimas У Javascript, безумовно, існує таке поняття, як знецінення. Стверджувати це, а потім підтримувати це, заявляючи, що старі браузери завжди повинні підтримуватися, - це абсолютно неісторична перспектива. У будь-якому випадку, будь-хто, хто хоче використовувати це, а також хоче уникати, unescape()може використовувати decodeURIComponent()замість цього. У цьому випадку це працює однаково. Однак я б рекомендував підхід radicand, оскільки він простіший, настільки ж підтримуваний і швидший у виконанні, з однаковими результатами (проте, не забудьте прочитати коментарі).
mechalynx

21

Слід зазначити , що використання unescape()є застарілим і не працює з компілятором машинопису, наприклад.

На основі відповіді radicand та розділу коментарів нижче, ось оновлене рішення:

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

http://example.com


Це не працює для деяких рядків, оскільки лапки можуть порушити рядок JSON і призвести до помилок синтаксичного аналізу JSON. Я використовував іншу відповідь ( stackoverflow.com/a/7885499/249327 ) у цих випадках.
nickdos

2

У мене недостатньо представників, щоб помістити це під коментарі до існуючих відповідей:

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

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


1

Використання JSON.decodeдля цього має суттєві недоліки, про які ви повинні знати:

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

Є й інші застереження. По суті, використання JSON.decodeдля цієї мети - хакерство і працює не так, як ви завжди могли очікувати. Вам слід дотримуватися використання JSONбібліотеки для обробки JSON, а не для рядкових операцій.


Нещодавно я сам зіткнувся з цим питанням і хотів надійний декодер, тому в підсумку сам написав його. Він повністю і ретельно протестований і доступний тут: https://github.com/iansan5653/unraw . Він максимально імітує стандарт JavaScript.

Пояснення:

Джерело складає близько 250 рядків, тому я не буду включати все це сюди, але по суті він використовує наступний регулярний вираз для пошуку всіх послідовностей виходу, а потім аналізує їх, використовуючи 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

Коментується (ПРИМІТКА. Цей регулярний вираз відповідає всім послідовностям екранування, включаючи недійсні. Якщо рядок '\x!!'видасть помилку в JS, вона видасть помилку в моїй бібліотеці [тобто помилка буде]):

/
\\ # 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.