Wie kehrt man eine Zeichenkette in JavaScript an Ort und Stelle um, wenn sie an eine Funktion mit einer Rückgabeanweisung übergeben wird, ohne integrierte Funktionen zu verwenden ( .reverse()
, .charAt()
usw.)?
Antworten
Zu viele Anzeigen?Eine neue Option ist die Verwendung von Intl. Segmentierer die es Ihnen ermöglicht, die visuellen Grapheme (d. h. vom Benutzer wahrgenommene Zeicheneinheiten wie Emojis, Buchstaben usw.) aufzuteilen. Intl.Segmenter
ist derzeit ein Vorschlag für Stufe 4 und es gibt eine Polyfill zur Verfügung, wenn Sie es verwenden möchten. Es hat derzeit eine begrenzte Browserunterstützung, über die Sie weitere Informationen finden können aquí .
So wird die reverse()
Methode aussehen kann, wenn Sie die Intl.Segmenter
:
const reverse = str => {
const segmenter = new Intl.Segmenter("en", {granularity: 'grapheme'});
const segitr = segmenter.segment(str);
const segarr = Array.from(segitr, ({segment}) => segment).reverse();
return segarr.join('');
}
console.log(reverse('foo bar mañana manana')); // ananam anañam rab oof
console.log(reverse('This emoji is happy')); // yppah si ijome sihT
console.log(reverse('Text surrogate pair composite pair moo varient selector & ZWJ ')); // JWZ & rotceles tneirav oom riap etisopmoc riap etagorrus txeT
Die obigen Ausführungen schaffen eine segmenter
um Zeichenketten anhand ihrer visuellen Grapheme zu segmentieren/aufzuteilen. Aufruf von .segment()
über die segmenter
mit der Zeichenkette input gibt dann einen Iterator zurück, der Objekte der Form {segment, index, input, isWordLike}
. Die segment
Schlüssel dieses Objekts enthält das Zeichenkettensegment (d. h. das einzelne Graphem). Um den Iterator in ein Array umzuwandeln, verwenden wir Array.from()
auf den Iterator und extrahieren die segmentierten Grapheme, die mit .reverse()
. Zum Schluss fügen wir das Array wieder zu einer Zeichenkette zusammen, indem wir .join()
Es gibt auch eine andere Option, die Sie ausprobieren können, die eine bessere Browserunterstützung als Intl.Segmenter bietet, aber nicht so kugelsicher ist:
const reverse = str => Array.from(str.normalize('NFC')).reverse().join('');
Dies hilft bei Zeichen, die aus mehreren Codepunkten und Codeeinheiten bestehen. Wie bereits in anderen Antworten erwähnt, gibt es Probleme mit der Beibehaltung der Reihenfolge von zusammengesetzten und Surrogatpaaren in Zeichenketten wie 'foo bar mañana manana'
. Hier ist ein Surrogatpaar, das aus zwei Codeeinheiten besteht, und die letzte n
ist ein zusammengesetztes Paar, das aus zwei Unicode-Zeichen besteht, um ein Graphem zu bilden ( n
+ \= n
).
Um jedes Zeichen umzukehren, können Sie die .reverse()
Methode, die Teil des Array-Prototyps ist. Als .reverse()
auf ein Array angewendet wird, muss zunächst die Zeichenkette in ein Array von Zeichen umgewandelt werden. Typischerweise, .split('')
wird für diese Aufgabe verwendet, allerdings werden dabei Surrogatpaare aufgespalten, die aus mehreren Codeeinheiten bestehen (wie bereits in frühere Antworten ) :
>> ''.split('')
>> `["", ""]`
Wenn Sie stattdessen die Funktion String.prototype
's Symbol.Iterator Methode können Sie Ihre Surrogatpaare in Ihrem Array beibehalten, da diese über die Codepunkte und nicht über die Codeeinheiten Ihrer Zeichenfolge iteriert:
>> [...'']
>> [""]
Als nächstes müssen alle zusammengesetzten Zeichen in der Zeichenfolge behandelt werden. Zeichen, die aus zwei oder mehr Codepunkten bestehen, werden auch bei der Iteration aufgespalten:
>> [...'o']
>> ["o", ""]
Das obige Beispiel trennt das Basiszeichen (o) von der Diaresis, was nicht erwünscht ist. Der Grund dafür ist o
ist eine dekomponierte Version des Zeichens, die aus mehreren Codepunkten besteht. Um damit umzugehen, können Sie eine in ES6 eingeführte String-Methode verwenden, die als String.prototype.normalize()
. Diese Methode kann mehrere Code-Punkte in ihre zusammengesetzte kanonische Form indem wir "NFC" als Argument verwenden. Dies erlaubt uns, das zerlegte Zeichen umzuwandeln o
(o + Diaerese kombinieren ) in seine vorkomponierte Form ö
( lateinischer Kleinbuchstabe o mit Diaeresis ), die nur aus einem Codepunkt besteht. Aufruf von .normalize()
con "NFC"
versucht daher, mehrere Codepunkte durch einzelne Codepunkte zu ersetzen soweit möglich . Dadurch können Grapheme, die aus zwei Codepunkten bestehen, mit einem Codepunkt dargestellt werden.
>> [...'o'.normalize('NFC')]
>> ["ö"]
Als normalize('NFC')
ein Zeichen erzeugt, kann es unter anderen Zeichen sicher umgedreht werden. Wenn man sowohl die Spreizsyntax als auch die Normalisierung kombiniert, kann man erfolgreich Zeichenketten umkehren, z. B:
const reverse = str => Array.from(str.normalize('NFC')).reverse().join('');
console.log(reverse('foo bar mañana manana'));
console.log(reverse('This emoji is happy'));
Es gibt ein paar Fälle, in denen die obige Normalisierung+Iteration fehlschlägt. Zum Beispiel, das Zeichen (schweres schwarzes Herz ) besteht aus zwei Codepunkten. Der erste ist das Herz und der zweite ist der Variationsselektor-16 (U+FE0F), das dazu dient, eine Glyphenvariante für das vorhergehende Zeichen zu definieren. Auch andere Zeichen können ähnliche Probleme verursachen.
Ein weiterer Punkt, auf den man achten sollte, ist ZWJ (Zero-width joiner) Zeichen, die in einigen Schriften zu finden sind, einschließlich Emoji. Das Emoji besteht zum Beispiel aus den Emoji Mann, Frau und Junge, die jeweils durch ein ZWJ getrennt sind. Die obige Normalisierungs- und Iterationsmethode berücksichtigt dies ebenfalls nicht.
Infolgedessen ist die Verwendung von Intl.Segmenter
ist die bessere Wahl gegenüber diesen beiden Ansätzen. Derzeit hat Chrome auch seine eigene spezifische Segmentierungs-API, die als Intl.v8BreakIterator . Diese Segmentierungs-API ist nicht Standard und etwas, das Chrome einfach nur implementiert. Es kann sich also ändern und funktioniert in den meisten Browsern nicht, daher wird seine Verwendung nicht empfohlen. Wenn Sie jedoch neugierig sind, können Sie es so machen:
const reverse = str => {
const iterator = Intl.v8BreakIterator(['en'], {type: 'character'});
iterator.adoptText(str);
const arr = [];
let pos = iterator.first();
while (pos !== -1) {
const current = iterator.current();
const nextPos = iterator.next();
if (nextPos === -1) break;
const slice = str.slice(current, nextPos);
arr.unshift(slice);
}
return arr.join("");
}
console.log(reverse('foo bar mañana manana')); // ananam anañam rab oof
console.log(reverse('This emoji is happy')); // yppah si ijome sihT
console.log(reverse('Text surrogate pair composite pair moo varient selector & ZWJ ')); // JWZ & rotceles tneirav oom riap etisopmoc riap etagorrus txeT
Ich weiß, dass dies eine alte Frage ist, die bereits gut beantwortet wurde, aber zu meinem eigenen Vergnügen habe ich die folgende Umkehrfunktion geschrieben und dachte, ich würde sie mit anderen teilen, falls sie für jemand anderen nützlich ist. Sie verarbeitet sowohl Surrogatpaare als auch Kombinationszeichen:
function StringReverse (str)
{
var charArray = [];
for (var i = 0; i < str.length; i++)
{
if (i+1 < str.length)
{
var value = str.charCodeAt(i);
var nextValue = str.charCodeAt(i+1);
if ( ( value >= 0xD800 && value <= 0xDBFF
&& (nextValue & 0xFC00) == 0xDC00) // Surrogate pair)
|| (nextValue >= 0x0300 && nextValue <= 0x036F)) // Combining marks
{
charArray.unshift(str.substring(i, i+2));
i++; // Skip the other half
continue;
}
}
// Otherwise we just have a rogue surrogate marker or a plain old character.
charArray.unshift(str[i]);
}
return charArray.join('');
}
Alle Requisiten zu Mathias, Punycode, und verschiedene andere Referenzen für die Schule mich auf die Komplexität der Zeichenkodierung in JavaScript.
0 Stimmen
Sie dürfen also nicht .charAt() verwenden, um die Zeichen der Zeichenkette zu erhalten?
192 Stimmen
Das können Sie nicht. JavaScript-Zeichenfolgen sind unveränderlich, was bedeutet, dass der Speicher, der ihnen zugewiesen ist, nicht beschrieben werden kann, was echte "In-Place"-Umkehrungen unmöglich macht.
3 Stimmen
Re: crescentfresh's Kommentar siehe stackoverflow.com/questions/51185/
3 Stimmen
@crescentfresh Sie sollten das als neue Antwort posten.
0 Stimmen
@Irwin, es ist 2015 und charAt() wird immer noch nicht vollständig unterstützt. Wo es unterstützt wird, ist es langsam.
2 Stimmen
Umkehrung einer Zeichenkette auf 3 Arten in Javascript
0 Stimmen
var reverse = (str, rev = '') => str.length == 0 ? console.log(rev) : reverse((str.substr(0,str.length -1)), (rev + str.substr(str.length -1 , 1)));
0 Stimmen
string umkehren in js