Wie dekodiere ich einen String mit maskiertem Unicode?

86

Ich bin mir nicht sicher, wie das heißt, also habe ich Probleme, danach zu suchen. Wie kann ich einen String mit Unicode von http\u00253A\u00252F\u00252Fexample.combis http://example.commit JavaScript dekodieren ? Ich habe es versucht unescape, decodeURIund decodeURIComponentich denke, das einzige, was noch übrig ist, ist das Ersetzen von Strings.

BEARBEITEN: Die Zeichenfolge ist nicht typisiert, sondern eine Teilzeichenfolge aus einem anderen Code. Um das Problem zu lösen, müssen Sie mit so etwas beginnen:

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

Ich hoffe das zeigt, warum unescape () nicht funktioniert.

styfle
quelle
Woher kommt die Saite?
Cameron
@Cameron: Die Zeichenfolge stammt aus einem Skript, für das ich innerHTML aufgerufen habe. Deshalb funktioniert Alex 'Antwort nicht.
Styfle

Antworten:

107

Bearbeiten (12.10.2017) :

@MechaLynx und @ Kevin-Weber stellen fest, dass dies unescape()in Nicht-Browser-Umgebungen veraltet ist und in TypeScript nicht vorhanden ist. decodeURIComponentist ein Ersatz. Verwenden Sie für eine bessere Kompatibilität stattdessen Folgendes:

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

Ursprüngliche Antwort:

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

Sie können die gesamte Arbeit an auslagern JSON.parse

Radicand
quelle
6
Interessant. Ich musste Anführungszeichen hinzufügen. unescape(JSON.parse('"' + s + '"'));Was ist der Grund für die zusätzlichen Anführungszeichen? Ist JSON damit gültig?
Styfle
1
Beachten Sie, dass dies deutlich schneller zu sein scheint als der fromCharCodeAnsatz: jsperf.com/unicode-func-vs-json-parse
nrabinowitz
17
Wichtiger Hinweis zur Antwort von @ styfle: Nicht verwenden, JSON.parse('"' + s + '"')wenn JSON.parse('"' + s.replace('"', '\\"') + '"')stattdessen nicht vertrauenswürdige Daten verwendet werden, da sonst Ihr Code beschädigt wird, wenn die Eingabe Anführungszeichen enthält.
Ntninja
7
Tolle Antwort @ alexander255, aber Sie möchten tatsächlich Folgendes verwenden: JSON.parse ('"' + str.replace (/ \" / g, '\\ "' + '"'), um ALLE Vorkommen dieses Zeichens im gesamten zu ersetzen Zeichenfolge, anstatt eine zu ersetzen.
CS
2
Für diejenigen, die darauf stoßen und sich Sorgen machen, weil unescape()es veraltet ist, decodeURIComponent()funktioniert es unescape()in diesem Fall identisch , also ersetzen Sie es einfach durch das und Sie sind gut.
Mechalynx
115

UPDATE : Bitte beachten Sie, dass dies eine Lösung ist, die für ältere Browser oder Nicht-Browser-Plattformen gelten sollte und zu Unterrichtszwecken am Leben gehalten wird. Eine aktuellere Antwort finden Sie in der Antwort von @radicand unten.


Dies ist eine Unicode-Zeichenfolge mit Escapezeichen. Zuerst wurde die Zeichenfolge maskiert und dann mit Unicode codiert. So konvertieren Sie wieder zum Normalzustand:

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

Zur Erklärung: Ich benutze einen regulären Ausdruck, um zu suchen \u0025. Da ich jedoch nur einen Teil dieser Zeichenfolge für meine Ersetzungsoperation benötige, verwende ich Klammern, um den Teil zu isolieren, den ich wiederverwenden werde 0025. Dieser isolierte Teil wird als Gruppe bezeichnet.

Der giTeil am Ende des Ausdrucks gibt an, dass er mit allen Instanzen in der Zeichenfolge übereinstimmen soll, nicht nur mit der ersten, und dass bei der Übereinstimmung die Groß- und Kleinschreibung nicht berücksichtigt werden soll. Dies mag angesichts des Beispiels unnötig erscheinen, erhöht jedoch die Vielseitigkeit.

Um nun von einer Zeichenfolge zur nächsten zu konvertieren, muss ich einige Schritte für jede Gruppe jeder Übereinstimmung ausführen, und ich kann dies nicht durch einfaches Transformieren der Zeichenfolge tun. Hilfreicherweise kann die Operation String.replace eine Funktion akzeptieren, die für jede Übereinstimmung ausgeführt wird. Die Rückgabe dieser Funktion ersetzt die Übereinstimmung selbst in der Zeichenfolge.

Ich verwende den zweiten Parameter, den diese Funktion akzeptiert, nämlich die Gruppe, die ich verwenden muss, und transformiere ihn in die entsprechende utf-8-Sequenz. Verwenden Sie dann die integrierte unescapeFunktion, um den String in die richtige Form zu dekodieren.

Ioannis Karadimas
quelle
3
Vielen Dank. Können Sie uns etwas erklären, was Sie tun? Es sieht so aus, als ob der Regex nach einem \uPräfix und dann nach einer 4-stelligen Hexadezimalzahl (Buchstaben oder Zahlen) sucht . Wie funktioniert die Funktion in der Ersetzungsmethode?
Styfle
1
Sie haben Recht, das brauchte eine Erklärung, also habe ich meinen Beitrag aktualisiert. Genießen!
Ioannis Karadimas
1
Tolle Lösung. In meinem Fall codiere ich alle internationalen (nicht ASCII) Zeichen, die vom Server als Escape-Unicode gesendet werden, und verwende dann Ihre Funktion im Browser, um die Zeichen in die richtigen UTF-8-Zeichen zu dekodieren. Ich stellte fest, dass ich den folgenden regulären Ausdruck aktualisieren musste, um Zeichen aus allen Sprachen (dh Thai) zu erfassen:var r = /\\u([\d\w]{1,})/gi;
Nathan Hanna
2
Beachten Sie, dass dies deutlich langsamer zu sein scheint als der JSON.parseAnsatz: jsperf.com/unicode-func-vs-json-parse
nrabinowitz
1
@IoannisKaradimas Es gibt mit Sicherheit so etwas wie Verachtung in Javascript. Das zu behaupten und es dann zu unterstützen, indem man erklärt, dass ältere Browser immer unterstützt werden müssen, ist eine völlig ahistorische Perspektive. Auf jeden Fall jemand, der will , dies zu nutzen und will auch vermeiden , unescape()verwenden können , decodeURIComponent()statt. In diesem Fall funktioniert es identisch. Ich würde jedoch den Ansatz von radicand empfehlen, da er einfacher, genauso unterstützt und schneller auszuführen ist und dieselben Ergebnisse liefert (lesen Sie jedoch unbedingt die Kommentare).
Mechalynx
21

Beachten Sie, dass die Verwendung von unescape()ist veraltet und funktioniert nicht mit dem Typoskript Compiler, zum Beispiel.

Basierend auf der Antwort von radicand und dem Kommentarbereich unten ist hier eine aktualisierte Lösung:

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

http://example.com

Kevin Weber
quelle
Dies funktioniert bei einigen Zeichenfolgen nicht, da Anführungszeichen die JSON-Zeichenfolge unterbrechen und zu JSON-Analysefehlern führen können. In diesen Fällen habe ich die andere Antwort ( stackoverflow.com/a/7885499/249327 ) verwendet.
Nickdos
2

Ich habe nicht genug Repräsentanten, um dies unter Kommentare zu den vorhandenen Antworten zu setzen:

unescapewird nur für die Arbeit mit URIs (oder einem verschlüsselten utf-8) abgelehnt, was wahrscheinlich für die Bedürfnisse der meisten Menschen der Fall ist. encodeURIComponentkonvertiert einen js-String in Escape-UTF-8 und decodeURIComponentfunktioniert nur mit Escape- UTF-8-Bytes. Es wird ein Fehler für etwas decodeURIComponent('%a9'); // errorausgelöst, weil erweitertes ASCII nicht gültig ist (obwohl dies immer noch ein Unicode-Wert ist), während unescape('%a9'); // ©Sie Ihre Daten kennen müssen, wenn Sie decodeURIComponent verwenden.

decodeURIComponent funktioniert nicht "%C2"oder es wird kein einzelnes Byte überschritten, 0x7fda dies in utf-8 einen Teil eines Ersatzes angibt. Allerdings decodeURIComponent("%C2%A9") //gives you ©würde Unescape nicht richtig funktionieren // ©UND es würde keinen Fehler auslösen, so dass Unescape zu fehlerhaftem Code führen kann, wenn Sie Ihre Daten nicht kennen.

aamarks
quelle
1

Die Verwendung JSON.decodehierfür bringt erhebliche Nachteile mit sich, die Sie beachten müssen:

  • Sie müssen die Zeichenfolge in doppelte Anführungszeichen setzen
  • Viele Zeichen werden nicht unterstützt und müssen selbst maskiert werden. Zum Beispiel, um eine der folgenden vorbei JSON.decode(nach ihnen in doppelten Anführungszeichen Einwickeln) werden Fehler , obwohl diese sind alle gültig: \\n, \n, \\0,a"a
  • Hexadezimale Escapezeichen werden nicht unterstützt: \\x45
  • Unicode-Codepunktsequenzen werden nicht unterstützt: \\u{045}

Es gibt auch andere Einschränkungen. Im Wesentlichen ist die Verwendung JSON.decodefür diesen Zweck ein Hack und funktioniert nicht so, wie Sie es immer erwarten. Sie sollten die JSONBibliothek weiterhin für JSON verwenden, nicht für Zeichenfolgenoperationen.


Ich bin kürzlich selbst auf dieses Problem gestoßen und wollte einen robusten Decoder, also habe ich selbst einen geschrieben. Es ist vollständig und gründlich getestet und hier verfügbar: https://github.com/iansan5653/unraw . Es ahmt den JavaScript-Standard so genau wie möglich nach.

Erläuterung:

Die Quelle besteht aus ungefähr 250 Zeilen, daher werde ich hier nicht alles einfügen, aber im Wesentlichen wird der folgende Regex verwendet, um alle Escape-Sequenzen zu finden und sie dann zu analysieren parseInt(string, 16), um die Basis-16-Zahlen zu dekodieren und dann String.fromCodePoint(number)das entsprechende Zeichen zu erhalten:

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

Kommentiert (HINWEIS: Dieser reguläre Ausdruck entspricht allen Escape-Sequenzen, einschließlich ungültiger. Wenn die Zeichenfolge einen Fehler in JS auslösen würde, würde dies einen Fehler in meiner Bibliothek auslösen [dh '\x!!'Fehler verursachen]):

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

Beispiel

Verwenden dieser Bibliothek:

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
quelle