406 javascript string reverse
当传递给带有return语句的函数时,如何在JavaScript中反转字符串(或就地)?所有没有使用内置功能?.reverse()
,.charAt()
等等.
bel*_*qua 707
只要您处理简单的ASCII字符,并且您很乐意使用内置函数,这将有效:
function reverse(s){
return s.split("").reverse().join("");
}
Run Code Online (Sandbox Code Playgroud)
如果您需要支持UTF-16或其他多字节字符的解决方案,请注意此函数将提供无效的unicode字符串或看起来很有趣的有效字符串.你可能想要考虑这个答案.
Mat*_*ens 401
以下技术(或类似)通常用于在JavaScript中反转字符串:
// Don’t use this!
var naiveReverse = function(string) {
return string.split('').reverse().join('');
}
Run Code Online (Sandbox Code Playgroud)
事实上,到目前为止发布的所有答案都是这种模式的变体.但是,此解决方案存在一些问题.例如:
naiveReverse('foo bar');
// ? 'rab ?? oof'
// Where did the `` symbol go? Whoops!
Run Code Online (Sandbox Code Playgroud)
如果您想知道为什么会这样,请阅读JavaScript的内部字符编码.(TL; DR: is an astral symbol, and JavaScript exposes it as two separate code units.)
But there’s more:
// To see which symbols are being used here, check:
// http://mothereff.in/js-escapes#1ma%C3%B1ana%20man%CC%83ana
naiveReverse('mañana man?ana');
// ? 'ana?nam anañam'
// Wait, so now the tilde is applied to the `a` instead of the `n`? WAT.
Run Code Online (Sandbox Code Playgroud)
A good string to test string reverse implementations is the following:
'foo bar mañana man?ana'
Run Code Online (Sandbox Code Playgroud)
Why? Because it contains an astral symbol ()(由JavaScript中的代理对表示)和一个组合标记(
n?
在最后一个man?ana
实际上由两个符号组成:U + 006E LATIN SMALL LETTER N和U + 0303 COMBINING TILDE).
代理对出现的顺序不能反转,否则星形符号将不再出现在"反向"字符串中.这就是您??
在前一个示例的输出中看到这些标记的原因.
组合标记始终应用于上一个符号,因此您必须将主符号(U + 006E LATIN SMALL LETTER N)作为组合标记(U + 0303 COMBINING TILDE)整体处理.反转它们的顺序将使组合标记与字符串中的另一个符号配对.这就是为什么示例输出a?
而不是ñ
.
希望这能解释为什么到目前为止发布的所有答案都是错误的.
为了回答你的初始问题 - 如何[正确]反转JavaScript中的字符串 - ,我编写了一个能够识别Unicode的字符串反转的小型JavaScript库.它没有我刚才提到的任何问题.该图书馆名为Esrever ; 它的代码在GitHub上,几乎适用于任何JavaScript环境.它带有一个shell实用程序/二进制文件,因此如果需要,您可以轻松地从终端中反转字符串.
var input = 'foo bar mañana man?ana';
esrever.reverse(input);
// ? 'anan?am anañam rab oof'
Run Code Online (Sandbox Code Playgroud)
至于"就地"部分,请参阅其他答案.
小智 88
String.prototype.reverse_string=function() {return this.split("").reverse().join("");}
Run Code Online (Sandbox Code Playgroud)
要么
String.prototype.reverse_string = function() {
var s = "";
var i = this.length;
while (i>0) {
s += this.substring(i-1,i);
i--;
}
return s;
}
Run Code Online (Sandbox Code Playgroud)
Ani*_*ole 56
详细分析和十种不同的方法来反转字符串及其性能细节.
http://eddmann.com/posts/ten-ways-to-reverse-a-string-in-javascript/
这些实现的性能:
每个浏览器的最佳性能实现
以下是这些实现:
实施1:
function reverse(s) {
var o = '';
for (var i = s.length - 1; i >= 0; i--)
o += s[i];
return o;
}
Run Code Online (Sandbox Code Playgroud)
实施2:
function reverse(s) {
var o = [];
for (var i = s.length - 1, j = 0; i >= 0; i--, j++)
o[j] = s[i];
return o.join('');
}
Run Code Online (Sandbox Code Playgroud)
实施3:
function reverse(s) {
var o = [];
for (var i = 0, len = s.length; i <= len; i++)
o.push(s.charAt(len - i));
return o.join('');
}
Run Code Online (Sandbox Code Playgroud)
实施4:
function reverse(s) {
return s.split('').reverse().join('');
}
Run Code Online (Sandbox Code Playgroud)
实施5:
function reverse(s) {
var i = s.length,
o = '';
while (i > 0) {
o += s.substring(i - 1, i);
i--;
}
return o;
}
Run Code Online (Sandbox Code Playgroud)
实施6:
function reverse(s) {
for (var i = s.length - 1, o = ''; i >= 0; o += s[i--]) { }
return o;
}
Run Code Online (Sandbox Code Playgroud)
实施7:
function reverse(s) {
return (s === '') ? '' : reverse(s.substr(1)) + s.charAt(0);
}
Run Code Online (Sandbox Code Playgroud)
实施8:
function reverse(s) {
function rev(s, len, o) {
return (len === 0) ? o : rev(s, --len, (o += s[len]));
};
return rev(s, s.length, '');
}
Run Code Online (Sandbox Code Playgroud)
实施9:
function reverse(s) {
s = s.split('');
var len = s.length,
halfIndex = Math.floor(len / 2) - 1,
tmp;
for (var i = 0; i <= halfIndex; i++) {
tmp = s[len - i - 1];
s[len - i - 1] = s[i];
s[i] = tmp;
}
return s.join('');
}
Run Code Online (Sandbox Code Playgroud)
实施10
function reverse(s) {
if (s.length < 2)
return s;
var halfIndex = Math.ceil(s.length / 2);
return reverse(s.substr(halfIndex)) +
reverse(s.substr(0, halfIndex));
}
Run Code Online (Sandbox Code Playgroud)
小智 51
整个"逆转一个字符串到位"是一个过时的面试问题C程序员,他们采访过的人(为了复仇,也许?)会问.不幸的是,它是"In Place"部分不再有效,因为几乎所有托管语言(JS,C#等)中的字符串都使用不可变字符串,从而无法在不分配任何新内存的情况下移动字符串.
虽然上面的解决方案确实会反转字符串,但如果不分配更多内存,它们就不会这样做,因此不满足条件.您需要直接访问已分配的字符串,并能够操纵其原始内存位置以便能够将其反转到位.
就个人而言,我真的很讨厌这些面试问题,但遗憾的是,我相信我们会在未来几年继续看到它们.
Mic*_*ski 38
首先,用于Array.from()
将字符串转换为数组,然后Array.prototype.reverse()
反转数组,然后Array.prototype.join()
将其转换为字符串.
const reverse = str => Array.from(str).reverse().join('');
Run Code Online (Sandbox Code Playgroud)
Luk*_*uke 25
在ECMAScript 6中,您可以更快地反转字符串而不使用.split('')
split方法,扩展运算符如下:
var str = [...'racecar'].reverse().join('');
Run Code Online (Sandbox Code Playgroud)
nin*_*cko 19
好像我迟到了3年...
不幸的是你不能像我们指出的那样.请参阅JavaScript字符串是否不可变?我需要JavaScript中的"字符串构建器"吗?
您可以做的另一件好事是创建一个"视图"或"包装器",它接受一个字符串并重新实现您正在使用的字符串API的任何部分,但假装字符串是相反的.例如:
var identity = function(x){return x};
function LazyString(s) {
this.original = s;
this.length = s.length;
this.start = 0; this.stop = this.length; this.dir = 1; // "virtual" slicing
// (dir=-1 if reversed)
this._caseTransform = identity;
}
// syntactic sugar to create new object:
function S(s) {
return new LazyString(s);
}
//We now implement a `"...".reversed` which toggles a flag which will change our math:
(function(){ // begin anonymous scope
var x = LazyString.prototype;
// Addition to the String API
x.reversed = function() {
var s = new LazyString(this.original);
s.start = this.stop - this.dir;
s.stop = this.start - this.dir;
s.dir = -1*this.dir;
s.length = this.length;
s._caseTransform = this._caseTransform;
return s;
}
//We also override string coercion for some extra versatility (not really necessary):
// OVERRIDE STRING COERCION
// - for string concatenation e.g. "abc"+reversed("abc")
x.toString = function() {
if (typeof this._realized == 'undefined') { // cached, to avoid recalculation
this._realized = this.dir==1 ?
this.original.slice(this.start,this.stop) :
this.original.slice(this.stop+1,this.start+1).split("").reverse().join("");
this._realized = this._caseTransform.call(this._realized, this._realized);
}
return this._realized;
}
//Now we reimplement the String API by doing some math:
// String API:
// Do some math to figure out which character we really want
x.charAt = function(i) {
return this.slice(i, i+1).toString();
}
x.charCodeAt = function(i) {
return this.slice(i, i+1).toString().charCodeAt(0);
}
// Slicing functions:
x.slice = function(start,stop) {
// lazy chaining version of https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/slice
if (stop===undefined)
stop = this.length;
var relativeStart = start<0 ? this.length+start : start;
var relativeStop = stop<0 ? this.length+stop : stop;
if (relativeStart >= this.length)
relativeStart = this.length;
if (relativeStart < 0)
relativeStart = 0;
if (relativeStop > this.length)
relativeStop = this.length;
if (relativeStop < 0)
relativeStop = 0;
if (relativeStop < relativeStart)
relativeStop = relativeStart;
var s = new LazyString(this.original);
s.length = relativeStop - relativeStart;
s.start = this.start + this.dir*relativeStart;
s.stop = s.start + this.dir*s.length;
s.dir = this.dir;
//console.log([this.start,this.stop,this.dir,this.length], [s.start,s.stop,s.dir,s.length])
s._caseTransform = this._caseTransform;
return s;
}
x.substring = function() {
// ...
}
x.substr = function() {
// ...
}
//Miscellaneous functions:
// Iterative search
x.indexOf = function(value) {
for(var i=0; i<this.length; i++)
if (value==this.charAt(i))
return i;
return -1;
}
x.lastIndexOf = function() {
for(var i=this.length-1; i>=0; i--)
if (value==this.charAt(i))
return i;
return -1;
}
// The following functions are too complicated to reimplement easily.
// Instead just realize the slice and do it the usual non-in-place way.
x.match = function() {
var s = this.toString();
return s.apply(s, arguments);
}
x.replace = function() {
var s = this.toString();
return s.apply(s, arguments);
}
x.search = function() {
var s = this.toString();
return s.apply(s, arguments);
}
x.split = function() {
var s = this.toString();
return s.apply(s, arguments);
}
// Case transforms:
x.toLowerCase = function() {
var s = new LazyString(this.original);
s._caseTransform = ''.toLowerCase;
s.start=this.start; s.stop=this.stop; s.dir=this.dir; s.length=this.length;
return s;
}
x.toUpperCase = function() {
var s = new LazyString(this.original);
s._caseTransform = ''.toUpperCase;
s.start=this.start; s.stop=this.stop; s.dir=this.dir; s.length=this.length;
return s;
}
})() // end anonymous scope
Run Code Online (Sandbox Code Playgroud)
演示:
> r = S('abcABC')
LazyString
original: "abcABC"
__proto__: LazyString
> r.charAt(1); // doesn't reverse string!!! (good if very long)
"B"
> r.toLowerCase() // must reverse string, so does so
"cbacba"
> r.toUpperCase() // string already reversed: no extra work
"CBACBA"
> r + '-demo-' + r // natural coercion, string already reversed: no extra work
"CBAcba-demo-CBAcba"
Run Code Online (Sandbox Code Playgroud)
踢球者 - 以下是纯数学就地完成,只访问每个角色一次,并且只在必要时:
> 'demo: ' + S('0123456789abcdef').slice(3).reversed().slice(1,-1).toUpperCase()
"demo: EDCBA987654"
> S('0123456789ABCDEF').slice(3).reversed().slice(1,-1).toLowerCase().charAt(3)
"b"
Run Code Online (Sandbox Code Playgroud)
如果应用于非常大的字符串,如果您只采用相对较小的片段,则会产生显着的节省.
这是否值得(在大多数编程语言中像反转一样)在很大程度上取决于您的用例以及重新实现字符串API的效率.例如,如果您只想进行字符串索引操作,或者使用小slice
s或substr
s,这将节省您的空间和时间.但是,如果您计划打印大型反转切片或子串,那么节省的确可能很小,甚至比完整副本更糟糕.您的"反向"字符串也不具有该类型string
,但您可以通过原型设计伪造它.
上面的演示实现创建了一个ReversedString类型的新对象.它是原型的,因此效率很高,几乎只需要很少的工作量和最小的空间开销(共享原型定义).这是一个涉及延迟切片的惰性实现.一旦执行像一个函数.slice
或者.reversed
,它会执行索引数学.最后,当您提取数据时(通过隐式调用.toString()
或.charCodeAt(...)
其他内容),它将以"智能"方式应用这些数据,触及可能的最少数据.
注意:上面的字符串API是一个示例,可能无法完美实现.您还可以使用您需要的1-2个功能.
Hus*_*een 13
您可以通过多种方式来反转JavaScript中的字符串。我记下了我喜欢的三种方式。
方法1:使用反向功能:
function reverse(str) {
return str.split('').reverse().join('');
}
Run Code Online (Sandbox Code Playgroud)
方法2:遍历字符:
function reverse(str) {
let reversed = '';
for (let character of str) {
reversed = character + reversed;
}
return reversed;
}
Run Code Online (Sandbox Code Playgroud)
方法3:使用reduce函数:
function reverse(str) {
return str.split('').reduce((rev, char) => char + rev, '');
}
Run Code Online (Sandbox Code Playgroud)
我希望这有帮助 :)
小智 10
在一次采访中,我被要求在不使用任何变量或本机方法的情况下反转字符串.这是我最喜欢的实现:
function reverseString(str) {
return str === '' ? '' : reverseString(str.slice(1)) + str[0];
}
Run Code Online (Sandbox Code Playgroud)
Mah*_*ain 10
有多种方法,你可以检查以下,
1.传统的for循环(递增):
function reverseString(str){
let stringRev ="";
for(let i= 0; i<str.length; i++){
stringRev = str[i]+stringRev;
}
return stringRev;
}
alert(reverseString("Hello World!"));
Run Code Online (Sandbox Code Playgroud)
2.传统的for循环(递减):
function reverseString(str){
let revstr = "";
for(let i = str.length-1; i>=0; i--){
revstr = revstr+ str[i];
}
return revstr;
}
alert(reverseString("Hello World!"));
Run Code Online (Sandbox Code Playgroud)
3.使用for-of循环
function reverseString(str){
let strn ="";
for(let char of str){
strn = char + strn;
}
return strn;
}
alert(reverseString("Get well soon"));
Run Code Online (Sandbox Code Playgroud)
4.使用forEach/high order数组方法:
function reverseString(str){
let revSrring = "";
str.split("").forEach(function(char){
revSrring = char + revSrring;
});
return revSrring;
}
alert(reverseString("Learning JavaScript"));
Run Code Online (Sandbox Code Playgroud)
5. ES6标准:
function reverseString(str){
let revSrring = "";
str.split("").forEach(char => revSrring = char + revSrring);
return revSrring;
}
alert(reverseString("Learning JavaScript"));
Run Code Online (Sandbox Code Playgroud)
6.最新方式:
function reverseString(str){
return str.split("").reduce(function(revString, char){
return char + revString;
}, "");
}
alert(reverseString("Learning JavaScript"));
Run Code Online (Sandbox Code Playgroud)
7.您也可以使用以下方式获得结果,
function reverseString(str){
return str.split("").reduce((revString, char)=> char + revString, "");
}
alert(reverseString("Learning JavaScript"));
Run Code Online (Sandbox Code Playgroud)
Dr.*_*r.G 10
使用扩展运算符的清晰方法:
const reverseString = str => [...str].reverse().join('');
console.log(reverseString('ABC'));
Run Code Online (Sandbox Code Playgroud)
这是我认为最简单的方法
var reverse = function(str) {
var arr = [];
for (var i = 0, len = str.length; i <= len; i++) {
arr.push(str.charAt(len - i))
}
return arr.join('');
}
console.log(reverse('I want a '));
Run Code Online (Sandbox Code Playgroud)
var str = 'sample string';
[].map.call(str, function(x) {
return x;
}).reverse().join('');
Run Code Online (Sandbox Code Playgroud)
要么
var str = 'sample string';
console.log(str.split('').reverse().join(''));
Run Code Online (Sandbox Code Playgroud)
//输出:'gnirts elpmas'
如果您不想使用任何内置函数。尝试这个
var string = 'abcdefg';
var newstring = '';
for(let i = 0; i < string.length; i++){
newstring = string[i] += newstring;
}
console.log(newstring);
Run Code Online (Sandbox Code Playgroud)
正如其他人指出的那样,字符串是不可变的,因此您无法就地反转它。您需要生成一个新字符串。执行此操作的一个新选项是使用Intl.Segmenter,它允许您分割视觉字素(即:用户感知的字符单元,例如表情符号、字符等)。Intl.Segmenter
目前是第 4 阶段提案,如果您想使用它,可以使用它的polyfill 。目前它的浏览器支持有限,您可以在此处找到更多信息。
reverse()
如果您使用以下方法,该方法可能如下所示Intl.Segmenter
:
const reverse = str => {\n const segmenter = new Intl.Segmenter("en", {granularity: \'grapheme\'});\n const segitr = segmenter.segment(str);\n const segarr = Array.from(segitr, ({segment}) => segment).reverse();\n return segarr.join(\'\');\n}\n\nconsole.log(reverse(\'foo bar ma\xc3\xb1ana man\xcc\x83ana\')); // anan\xcc\x83am ana\xc3\xb1am rab oof\nconsole.log(reverse(\'This emoji is happy\')); // yppah si ijome sihT\nconsole.log(reverse(\'Text surrogate pair composite pair mo\xcc\x88o varient selector \xe2\x9d\xa4\xef\xb8\x8f & ZWJ \xe2\x80\x8d\xe2\x80\x8d\')); // \xe2\x80\x8d\xe2\x80\x8d JWZ & \xe2\x9d\xa4\xef\xb8\x8f rotceles tneirav oo\xcc\x88m riap etisopmoc riap etagorrus txeT
Run Code Online (Sandbox Code Playgroud)\r\n上面创建了一个segmenter
通过视觉字素来分段/分割字符串的方法。.segment()
使用字符串输入调用segmenter
,然后返回一个迭代器,该迭代器生成 形式的对象{segment, index, input, isWordLike}
。该对象的键segment
包含字符串段(即:单个字素)。要将迭代器转换为数组,我们使用Array.from()
迭代器并提取分段的字素,这可以用 反转.reverse()
。最后,我们使用以下方法将数组连接回字符串:.join()
您还可以尝试另一个选项,它比 Intl.Segmenter 具有更好的浏览器支持,但并不那么安全:
\nconst reverse = str => Array.from(str.normalize(\'NFC\')).reverse().join(\'\');\n
Run Code Online (Sandbox Code Playgroud)\n这有助于处理由多个代码点和代码单元组成的字符。正如其他答案中所指出的,在诸如 之类的字符串中维护复合和代理对排序存在问题\'foo bar ma\xc3\xb1ana man\xcc\x83ana\'
。这是由两个代码单元组成的代理对,最后一个
n\xcc\x83
是由两个 Unicode 字符组成的复合对,组成一个字素 ( n
+ \xcc\x83
= n\xcc\x83
)。
为了反转每个字符,您可以使用.reverse()
数组原型中的方法。正如.reverse()
在数组上使用的那样,首先要做的是将字符串转换为字符数组。通常,.split(\'\')
用于此任务,但是,这会分割由多个代码单元组成的代理对(如前面的答案所示):
>> \'\'.split(\'\')\n>> `["\xef\xbf\xbd", "\xef\xbf\xbd"]`\n
Run Code Online (Sandbox Code Playgroud)\n相反,如果您调用String.prototype
Symbol.iterator方法,那么您将能够在数组中保留代理对,因为它会迭代代码点而不是字符串的代码单元:
>> [...\'\']\n>> [""]\n
Run Code Online (Sandbox Code Playgroud)\n接下来要处理的是字符串中的任何复合字符。迭代时,由两个或多个代码点组成的字符仍将被分割:
\n>> [...\'o\xcc\x88\'] \n>> ["o", "\xcc\x88"]\n
Run Code Online (Sandbox Code Playgroud)\n上面将基本字符 (o) 与分音符分开,这不是所需的行为。这是因为它o\xcc\x88
是字符的分解版本,由多个代码点组成。为了解决这个问题,您可以使用 ES6 中引入的字符串方法,称为String.prototype.normalize()
. 该方法可以通过使用“NFC”作为参数将多个代码点组合成其组合的规范形式。这允许我们将分解的字符o\xcc\x88
(o +结合分音符)转换为其仅由一个代码点组成的预组合形式\xc3\xb6
(拉丁小写字母 o 与分音符)。因此,在可能的情况下,调用.normalize()
with"NFC"
会尝试用单个代码点替换多个代码点。这使得由两个代码点组成的字素可以用一个代码点来表示。
>> [...\'o\xcc\x88\'.normalize(\'NFC\')] \n>> ["\xc3\xb6"]\n
Run Code Online (Sandbox Code Playgroud)\n当normalize(\'NFC\')
生成一个字符时,它可以在其他字符中安全地反转。将扩展语法和规范化结合在一起,您可以成功反转字符串,例如:
const reverse = str => Array.from(str.normalize(\'NFC\')).reverse().join(\'\');\n
Run Code Online (Sandbox Code Playgroud)\r\n在某些情况下,上述归一化+迭代会失败。例如,字符 \xe2\x9d\xa4\xef\xb8\x8f (heavy black heart \xe2\x9d\xa4\xef\xb8\x8f
) 由两个代码点组成。第一个是心形,后者是变体选择器 16 (U+FE0F),用于定义前一个字符的字形变体。其他角色也会产生类似的问题。
另一件需要注意的事情是ZWJ(零宽度连接符)字符,您可以在某些脚本中找到这些字符,包括表情符号。例如,表情符号 \xe2\x80\x8d\xe2\x80\x8d 由男人、女人和男孩表情符号组成,每个表情符号由 ZWJ 分隔。上面的归一化+迭代方法也无法解释这一点。
\n因此,使用Intl.Segmenter
是比这两种方法更好的选择。目前,Chrome 还拥有自己的特定分段 API,称为Intl.v8BreakIterator。这个分段 API 是非标准的,Chrome 只是简单实现的。因此,它可能会发生变化,并且不适用于大多数浏览器,因此不建议使用。但是,如果您好奇,可以这样做:
>> \'\'.split(\'\')\n>> `["\xef\xbf\xbd", "\xef\xbf\xbd"]`\n
Run Code Online (Sandbox Code Playgroud)\r\n我知道这是一个老问题已经得到很好的回答,但为了我自己的乐趣,我编写了以下反向功能,并认为我会分享它,以防它对其他人有用.它处理代理对和组合标记:
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('');
}
Run Code Online (Sandbox Code Playgroud)
所有关于Mathias,Punycode和其他各种参考资料的道具都是为了让我了解JavaScript中字符编码的复杂性.
小智 5
在ES6中,您还有一个选择
function reverseString (str) {
return [...str].reverse().join('')
}
reverseString('Hello');
Run Code Online (Sandbox Code Playgroud)
你不能,因为 JS 字符串是不可变的。简短的非就地解决方案
[...str].reverse().join``
Run Code Online (Sandbox Code Playgroud)
[...str].reverse().join``
Run Code Online (Sandbox Code Playgroud)
你不能string
就地反转 a 但你可以使用这个:
String.prototype.reverse = function() {
return this.split("").reverse().join("");
}
var s = "ABCD";
s = s.reverse();
console.log(s);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
397726 次 |
最近记录: |