如何就地反转字符串?
当字符串传递给带有 return 语句的函数时,如何在 JavaScript 中就地反转字符串,而不使用内置函数(.reverse()
、.charAt()< /代码>等)?
How do you reverse a string in-place in JavaScript when it is passed to a function with a return statement, without using built-in functions (.reverse()
, .charAt()
etc.)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
只要您处理简单的 ASCII 字符,并且您乐于使用内置函数,这就会起作用:
如果您需要支持 UTF-16 或其他多字节字符的解决方案,请注意此函数将给出无效的 unicode 字符串,或者看起来很有趣的有效字符串。 您可能需要考虑这个答案。
数组扩展运算符支持 Unicode:
另一个使用
split()
的 Unicode 感知解决方案,如 MDN 是使用正则表达式,并将u
(Unicode) 标志设置为分隔符。As long as you're dealing with simple ASCII characters, and you're happy to use built-in functions, this will work:
If you need a solution that supports UTF-16 or other multi-byte characters, be aware that this function will give invalid unicode strings, or valid strings that look funny. You might want to consider this answer instead.
The array expansion operator is Unicode aware:
Another Unicode aware solution using
split()
, as explained on MDN, is to use a regexp with theu
(Unicode) flag set as a separator.以下技术(或类似技术)通常用于反转 JavaScript 中的字符串:
事实上,到目前为止发布的所有答案都是此模式的变体。 然而,这个解决方案存在一些问题。 例如:
The following technique (or similar) is commonly used to reverse a string in JavaScript:
In fact, all the answers posted so far are a variation of this pattern. However, there are some problems with this solution. For example:
If you’re wondering why this happens, read up on JavaScript’s internal character encoding. (TL;DR:
????
is an astral symbol, and JavaScript exposes it as two separate code units.)But there’s more:
A good string to test string reverse implementations is the following:
Why? Because it contains an astral symbol (
????
) (which are represented by surrogate pairs in JavaScript) and a combining mark (theñ
in the lastmañana
actually consists of two symbols: U+006E LATIN SMALL LETTER N and U+0303 COMBINING TILDE).The order in which surrogate pairs appear cannot be reversed, else the astral symbol won’t show up anymore in the ‘reversed’ string. That’s why you saw those
��
marks in the output for the previous example.Combining marks always get applied to the previous symbol, so you have to treat both the main symbol (U+006E LATIN SMALL LETTER N) as the combining mark (U+0303 COMBINING TILDE) as a whole. Reversing their order will cause the combining mark to be paired with another symbol in the string. That’s why the example output had
ã
instead ofñ
.Hopefully, this explains why all the answers posted so far are wrong.
To answer your initial question — how to [properly] reverse a string in JavaScript —, I’ve written a small JavaScript library that is capable of Unicode-aware string reversal. It doesn’t have any of the issues I just mentioned. The library is called Esrever; its code is on GitHub, and it works in pretty much any JavaScript environment. It comes with a shell utility/binary, so you can easily reverse strings from your terminal if you want.
As for the “in-place” part, see the other answers.
或者
or
详细分析和反转字符串的十种不同方法及其性能细节。
http://eddmann.com/posts/ten- ways-to-reverse-a-string-in-javascript/
这些实现的性能:
每个浏览器的最佳性能实现
以下是这些实现:
实现 1:
实现 2:
实现 3:
实现 4 :
实施5:
实施6:
实施7:
实施8:
实施9 :
实施10
实施11
Detailed analysis and ten different ways to reverse a string and their performance details.
http://eddmann.com/posts/ten-ways-to-reverse-a-string-in-javascript/
Perfomance of these implementations:
Best performing implementation(s) per browser
Here are those implementations:
Implementation 1:
Implementation 2:
Implementation 3:
Implementation 4:
Implementation 5:
Implementation 6:
Implementation 7:
Implementation 8:
Implementation 9:
Implementation 10
Implementation 11
整个“反转字符串”是 C 程序员的一个过时的面试问题,接受他们面试的人(也许是为了报复?)会问。 不幸的是,“就地”部分不再起作用,因为几乎所有托管语言(JS、C# 等)中的字符串都使用不可变字符串,从而破坏了在不分配任何新内存的情况下移动字符串的整个想法。
虽然上面的解决方案确实反转了字符串,但它们在不分配更多内存的情况下不会这样做,因此不满足条件。 您需要直接访问分配的字符串,并且能够操作其原始内存位置以便能够将其反转。
就我个人而言,我真的很讨厌这类面试问题,但可悲的是,我确信我们在未来几年还会继续看到它们。
The whole "reverse a string in place" is an antiquated interview question C programmers, and people who were interviewed by them (for revenge, maybe?), will ask. Unfortunately, it's the "In Place" part that no longer works because strings in pretty much any managed language (JS, C#, etc) uses immutable strings, thus defeating the whole idea of moving a string without allocating any new memory.
While the solutions above do indeed reverse a string, they do not do it without allocating more memory, and thus do not satisfy the conditions. You need to have direct access to the string as allocated, and be able to manipulate its original memory location to be able to reverse it in place.
Personally, i really hate these kinds of interview questions, but sadly, i'm sure we'll keep seeing them for years to come.
首先,使用
Array.from()
将字符串转换为数组,然后Array.prototype.reverse()
反转数组,然后Array.prototype.join()
使其返回字符串。First, use
Array.from()
to turn a string into an array, thenArray.prototype.reverse()
to reverse the array, and thenArray.prototype.join()
to make it back a string.在 ECMAScript 6 中,您可以更快地反转字符串,而无需使用
.split('')
split 方法,使用 传播运算符 像这样:In ECMAScript 6, you can reverse a string even faster without using
.split('')
split method, with the spread operator like so:看来我迟到了三年……
不幸的是你不能,正如已经指出的那样。 请参阅 JavaScript 字符串是不可变的吗? 我需要 JavaScript 中的“字符串构建器”吗?
您可以做的下一个最好的事情是创建一个“视图”或“包装器”,它接受一个字符串并重新实现您正在使用的字符串 API 的任何部分,但假装字符串颠倒了。 例如:
演示:
踢球者 - 以下是通过纯数学就地完成的,仅访问每个字符一次,并且仅在必要时才访问:
如果应用于非常大的字符串,如果您只采用相对较大的字符串,则这会产生显着的节省。一小片。
这是否值得(而不是像大多数编程语言那样以副本形式反转)在很大程度上取决于您的用例以及重新实现字符串 API 的效率。 例如,如果您只想进行字符串索引操作,或者采用小切片或子字符串,这将节省您的空间和时间。 然而,如果您计划打印大的反向切片或子字符串,节省的成本可能确实很小,甚至比完成完整副本还要糟糕。 您的“反转”字符串也不会具有
string
类型,尽管您可以通过原型设计来伪造它。上面的演示实现创建了一个 ReversedString 类型的新对象。 它是原型化的,因此相当高效,几乎只需最少的工作和最小的空间开销(原型定义是共享的)。 这是一个涉及延迟切片的惰性实现。 每当您执行
.slice
或.reversed
等函数时,它都会执行索引数学运算。 最后,当您提取数据时(通过隐式调用.toString()
或.charCodeAt(...)
等),它将以“智能”方式应用这些数据,尽可能少地接触数据。注意:上面的字符串API只是一个示例,可能无法完美实现。 您也可以只使用您需要的 1-2 个功能。
Seems like I'm 3 years late to the party...
Unfortunately you can't as has been pointed out. See Are JavaScript strings immutable? Do I need a "string builder" in JavaScript?
The next best thing you can do is to create a "view" or "wrapper", which takes a string and reimplements whatever parts of the string API you are using, but pretending the string is reversed. For example:
Demo:
The kicker -- the following is done in-place by pure math, visiting each character only once, and only if necessary:
This yields significant savings if applied to a very large string, if you are only taking a relatively small slice thereof.
Whether this is worth it (over reversing-as-a-copy like in most programming languages) highly depends on your use case and how efficiently you reimplement the string API. For example if all you want is to do string index manipulation, or take small
slice
s orsubstr
s, this will save you space and time. If you're planning on printing large reversed slices or substrings however, the savings may be small indeed, even worse than having done a full copy. Your "reversed" string will also not have the typestring
, though you might be able to fake this with prototyping.The above demo implementation creates a new object of type ReversedString. It is prototyped, and therefore fairly efficient, with almost minimal work and minimal space overhead (prototype definitions are shared). It is a lazy implementation involving deferred slicing. Whenever you perform a function like
.slice
or.reversed
, it will perform index mathematics. Finally when you extract data (by implicitly calling.toString()
or.charCodeAt(...)
or something), it will apply those in a "smart" manner, touching the least data possible.Note: the above string API is an example, and may not be implemented perfectly. You also can use just 1-2 functions which you need.
使用扩展语法的清晰方式:
Legible way using spread syntax:
在 JavaScript 中可以通过多种方式反转字符串。 我记下了三种我喜欢的方式。
方法1:使用reverse函数:
方法2:循环字符:
方法3:使用reduce函数:
我希望这会有所帮助:)
There are many ways you can reverse a string in JavaScript. I'm jotting down three ways I prefer.
Approach 1: Using reverse function:
Approach 2: Looping through characters:
Approach 3: Using reduce function:
I hope this helps :)
有多种方法可以做到这一点,您可以检查以下内容,
1. 传统的 for 循环(递增):
2. 传统的 for 循环(递减):
3. 使用 for-of 循环
4. 使用forEach/高阶数组方法:
5. ES6 标准:
6. 最新方式:
7. 您还可以使用以下方法获得结果,
There are Multiple ways of doing it, you may check the following,
1. Traditional for loop(incrementing):
2. Traditional for loop(decrementing):
3. Using for-of loop
4. Using the forEach/ high order array method:
5. ES6 standard:
6. The latest way:
7. You may also get the result using the following,
在一次采访中,我被要求在不使用任何变量或本机方法的情况下反转字符串。 这是我最喜欢的实现:
During an interview, I was asked to reverse a string without using any variables or native methods. This is my favorite implementation:
在 ES6 中,你多了一种选择
In ES6, you have one more option
这是我认为最简单的方法
This is the easiest way I think
或
// 输出:'gnirts elpmas'
OR
// Output: 'gnirts elpmas'
如果您不想使用任何内置函数。 尝试这个
If you don't want to use any built in function. Try this
正如其他人指出的那样,字符串是不可变的,因此您无法就地反转它。 您需要生成一个新字符串。 执行此操作的一个新选项是使用 Intl .Segmenter 允许您分割视觉字素(即:用户感知的字符单元,例如表情符号、字符等)。
Intl.Segmenter
目前是第 4 阶段提案,并且如果您想使用它,则可以使用 polyfill 。 目前它的浏览器支持有限,您可以找到有关 此处。如果您使用
Intl.Segmenter
,reverse()
方法可能如下所示:As other's have pointed out, strings are immutable, so you cannot reverse it in-place. You'll need to produce a new string instead. One new option to do this is to use Intl.Segmenter which allows you to split on the visual graphemes (ie: user-perceived character units such as emojis, characters, etc.).
Intl.Segmenter
is currently a stage 4 proposal and there is a polyfill available for it if you wish to use it. It currently has limited browser support which you can find more information about here.Here is how the
reverse()
method may look if you useIntl.Segmenter
:The above creates a
segmenter
to segment/split strings by their visual graphemes. Calling.segment()
on thesegmenter
with the string input then returns an iterator, which produces objects of the form{segment, index, input, isWordLike}
. Thesegment
key from this object contains the string segment (ie: the individual grapheme). To convert the iterator to an array, we useArray.from()
on the iterator and extract the segmented graphemes, which can be reversed with.reverse()
. Lastly, we join the array back into a string using.join()
There is also another option which you can try that has better browser support than Intl.Segmenter, however isn't as bullet-proof:
this helps deal with characters consisting of multiple code points and code units. As pointed out in other answers, there are issues with maintaining composite and surrogate pair ordering in strings such as
'foo ???? bar mañana mañana'
. Here????
is a surrogate pair consisting of two code units, and the lastñ
is a composite pair consisting of two Unicode characters to make up one grapheme (n
+̃
=ñ
).In order to reverse each character, you can use the
.reverse()
method which is part of the Array prototype. As.reverse()
is used on an array, the first thing to do is to turn the string into an array of characters. Typically,.split('')
is used for this task, however, this splits up surrogate pairs which are made from up of multiple code units (as already shown in previous answers):Instead, if you invoke the
String.prototype
's Symbol.iterator method then you'll be able to retain your surrogate pairs within your array, as this iterates over the code points rather than the code units of your string:The next thing to handle is any composite characters within the string. Characters that consist of two or more code points will still be split when iterated on:
The above separates the base character (o) from the diaresis, which is not desired behavior. This is because
ö
is a decomposed version of the character, consisting of multiple code points. To deal with this, you can use a string method introduced in ES6 known asString.prototype.normalize()
. This method can compose multiple code points into its composed canonical form by using "NFC" as an argument. This allows us to convert the decomposed characterö
(o + combining diaeresis) into its precomposed formö
(latin small letter o with diaeresis) which consists of only one code point. Calling.normalize()
with"NFC"
thus tries to replace multiple code points with single code points where possible. This allows graphemes consisting of two code points to be represented with one code point.As
normalize('NFC')
produces one character, it can then be reversed safely when amongst others. Putting both the spread syntax and normalization together, you can successfully reverse strings of characters such as:There are a few cases where the above normalization+iteration will fail. For instance, the character ❤️ (heavy black heart
❤️
) consists of two code points. The first being the heart and the latter being the variation selector-16 (U+FE0F) which is used to define a glyph variant for the preceding character. Other characters can also produce similar issues like this.Another thing to look out for is ZWJ (Zero-width joiner) characters, which you can find in some scripts, including emoji. For example the emoji ???????????? comprises of the Man, Woman and Boy emoji, each separated by a ZWJ. The above normalization + iteration method will not account for this either.
As a result, using
Intl.Segmenter
is the better choice over these two approaches. Currently, Chrome also has its own specific segmentation API known as Intl.v8BreakIterator. This segmentation API is nonstandard and something that Chrome simply just implements. So, it is subject to change and doesn't work on most browsers, so it's not recommended to use. However, if you're curious, this is how it could be done:我知道这是一个已经得到很好回答的老问题,但为了我自己的娱乐,我编写了以下反向函数,并认为我会分享它,以防它对其他人有用。 它处理代理对和组合标记:
所有对 Mathias、Punycode 和其他各种参考资料的支持,让我了解 JavaScript 中字符编码的复杂性。
I know that this is an old question that has been well answered, but for my own amusement I wrote the following reverse function and thought I would share it in case it was useful for anyone else. It handles both surrogate pairs and combining marks:
All props to Mathias, Punycode, and various other references for schooling me on the complexities of character encoding in JavaScript.
你不能,因为 JS 字符串是不可变的。 短非就地解决方案
You can't because JS strings are immutable. Short non-in-place solution
您无法就地反转
string
但您可以使用以下命令:You can't reverse a
string
in place but you can use this:UTF-8 字符串可以有:
b
字符和由 unicode 转义序列生成的后续~
变音符号组成的b̃
\u0303
;UTF-8 strings can have:
b̃
which composed of theb
character and a following~
diacritic generated by the unicode escape sequnce\u0303
;????
; which is generated by the multi-byte unicode escape sequence\uD83C\uDFA5
; and\u200D
). For example, the character????????????
can be composed using the individual (multi-byte) emojis ???? then a zero-width joiner then ???? then another zero-width joiner then ???? such that the entire 3-person character is 8-bytes (\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66
).This will handle reversing all 3 cases and keeping the bytes in the correct order such that the characters are reversed (rather than naively reversing the bytes of the string):
Update
The above code identifies some of the more commonly used combining diacritics. A more complete list of combining diacritics (that could be swapped into the above code) is:
我认为 String.prototype.reverse 是解决这个问题的好方法;
代码如下;
I think String.prototype.reverse is a good way to solve this problem;
the code as below;
真正的答案是:您无法就地反转它,但您可以创建一个相反的新字符串。
就像递归的练习一样:有时当你去面试时,面试官可能会问你如何使用递归来做到这一点,我认为“首选答案”可能是“我宁愿不在递归中这样做,因为它”很容易导致堆栈溢出”(因为它是
O(n)
而不是O(log n)
。如果是O(log n)
code>,堆栈溢出是相当困难的——32级堆栈可以处理40亿个项目,因为2 ** 32是4294967296。但是如果是O(n)
,那么它很容易出现堆栈溢出。有时面试官仍然会问你,“只是作为练习,你为什么不仍然使用递归来编写它?”这是:
测试运行:
输出:
尝试获取一个堆栈溢出,我在 Google Chrome 中将
1000
更改为10000
,结果报告:The real answer is: you can't reverse it in place, but you can create a new string that is the reverse.
Just as an exercise to play with recursion: sometimes when you go to an interview, the interviewer may ask you how to do this using recursion, and I think the "preferred answer" might be "I would rather not do this in recursion as it can easily cause a stack overflow" (because it is
O(n)
rather thanO(log n)
. If it isO(log n)
, it is quite difficult to get a stack overflow -- 4 billion items could be handled by a stack level of 32, as 2 ** 32 is 4294967296. But if it isO(n)
, then it can easily get a stack overflow.Sometimes the interviewer will still ask you, "just as an exercise, why don't you still write it using recursion?" And here it is:
test run:
output:
To try getting a stack overflow, I changed
1000
to10000
in Google Chrome, and it reported:字符串本身是不可变的,但您可以使用以下代码轻松创建反向副本:
Strings themselves are immutable, but you can easily create a reversed copy with the following code:
使用内置函数反转字符串
在没有助手的情况下反转字符串
Reverse a String using built-in functions
Reverse a String without the helpers
使用数组函数,
Using Array functions,
我自己最初的尝试...
http://jsbin.com/bujiwo/19/编辑?js,控制台,输出
My own original attempt...
http://jsbin.com/bujiwo/19/edit?js,console,output