如何在Go中反转string?
我们如何在Go中反转一个简单的string?
在Go1符文是内buildtypes。
func Reverse(s string) string { runes := []rune(s) for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { runes[i], runes[j] = runes[j], runes[i] } return string(runes) }
build议golang-nuts邮件列表中的Russ Coxbuild议
package main import "fmt" func main() { input := "The quick brown 狐 jumped over the lazy 犬" // Get Unicode code points. n := 0 rune := make([]rune, len(input)) for _, r := range input { rune[n] = r n++ } rune = rune[0:n] // Reverse for i := 0; i < n/2; i++ { rune[i], rune[n-1-i] = rune[n-1-i], rune[i] } // Convert back to UTF-8. output := string(rune) fmt.Println(output) }
这工作在unicodestring通过考虑2件事情:
- 范围通过枚举unicode字符在string上工作
- string可以由int切片构成,其中每个元素是一个unicode字符。
所以在这里:
func reverse(s string) string { o := make([]int, utf8.RuneCountInString(s)); i := len(o); for _, c := range s { i--; o[i] = c; } return string(o); }
这个工作,没有所有的function:
func Reverse(s string) (result string) { for _,v := range s { result = string(v) + result } return }
当Simon发布他的解决scheme时,我注意到了这个问题,因为string是不可变的,效率非常低。 其他提出的解决scheme也是有缺陷的; 他们不工作,或者效率低下。
这是一个有效的解决scheme,除非string不是有效的UTF-8或string包含组合字符。
package main import "fmt" func Reverse(s string) string { n := len(s) runes := make([]rune, n) for _, rune := range s { n-- runes[n] = rune } return string(runes[n:]) } func main() { fmt.Println(Reverse(Reverse("Hello, 世界"))) fmt.Println(Reverse(Reverse("The quick brown 狐 jumped over the lazy 犬"))) }
Go示例项目: Andrew Gerrand的golang / example / stringutil / reverse.go
/* Copyright 2014 Google Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // Reverse returns its argument string reversed rune-wise left to right. func Reverse(s string) string { r := []rune(s) for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { r[i], r[j] = r[j], r[i] } return string(r) }
去游乐场倒转一个string
反转string“bròwn”后,正确的结果应该是“nwòrb”,而不是“nẁorb”。
注意字母o上方的坟墓。
为了保留Unicode结合字符,例如“as⃝df̅”和反向结果“f̅ds⃝a”,
请参考下面列出的其他代码:
这里有太多的答案。 其中一些是明确的重复。 但即使从左边的一个,也很难select最好的解决scheme。
所以我通过答案,扔掉了一个不工作的Unicode,也删除重复。 我以幸存者为基准find最快的。 所以这里是归属地的结果 (如果你注意到我错过的答案,但值得添加的话,可以随意修改基准):
Benchmark_rmuller-4 100000 19246 ns/op Benchmark_peterSO-4 50000 28068 ns/op Benchmark_russ-4 50000 30007 ns/op Benchmark_ivan-4 50000 33694 ns/op Benchmark_yazu-4 50000 33372 ns/op Benchmark_yuku-4 50000 37556 ns/op Benchmark_simon-4 3000 426201 ns/op
所以这里是rmuller最快的方法 :
func Reverse(s string) string { size := len(s) buf := make([]byte, size) for start := 0; start < size; { r, n := utf8.DecodeRuneInString(s[start:]) start += n utf8.EncodeRune(buf[size-start:], r) } return string(buf) }
出于某种原因,我不能添加基准,所以你可以从PlayGround复制它(你不能在那里运行testing)。 重命名它并运行go test -bench=.
我写了以下Reverse
函数,尊重UTF8编码和组合字符:
// Reverse reverses the input while respecting UTF8 encoding and combined characters func Reverse(text string) string { textRunes := []rune(text) textRunesLength := len(textRunes) if textRunesLength <= 1 { return text } i, j := 0, 0 for i < textRunesLength && j < textRunesLength { j = i + 1 for j < textRunesLength && isMark(textRunes[j]) { j++ } if isMark(textRunes[j-1]) { // Reverses Combined Characters reverse(textRunes[i:j], ji) } i = j } // Reverses the entire array reverse(textRunes, textRunesLength) return string(textRunes) } func reverse(runes []rune, length int) { for i, j := 0, length-1; i < length/2; i, j = i+1, j-1 { runes[i], runes[j] = runes[j], runes[i] } } // isMark determines whether the rune is a marker func isMark(r rune) bool { return unicode.Is(unicode.Mn, r) || unicode.Is(unicode.Me, r) || unicode.Is(unicode.Mc, r) }
我尽我所能使其尽可能高效和可读。 这个想法很简单,遍历寻找组合字符的符文,然后在原地颠倒组合字符的符文。 一旦我们把所有的东西全部覆盖起来,就可以将整个string的符文颠倒过来。
说我们想扭转这个stringbròwn
。 ò
用两个符文表示,一个代表“坟墓”,一个代表这个“unicode”。
为了简单起见,让我们来表示这样的string。 我们所做的第一件事就是寻找组合字符并将其反转。 所以现在我们有stringbr'own
。 最后,我们扭转整个string,并以nwo'rb
结束。 这是作为nwòrb
返回给我们的
如果你想使用它,你可以在https://github.com/shomali11/utilfind它。;
这里有一些testing用例来展示几种不同的场景:
func TestReverse(t *testing.T) { assert.Equal(t, Reverse(""), "") assert.Equal(t, Reverse("X"), "X") assert.Equal(t, Reverse("b\u0301"), "b\u0301") assert.Equal(t, Reverse("😎⚽"), "⚽😎") assert.Equal(t, Reverse("Les Mise\u0301rables"), "selbare\u0301siM seL") assert.Equal(t, Reverse("ab\u0301cde"), "edcb\u0301a") assert.Equal(t, Reverse("This `\xc5` is an invalid UTF8 character"), "retcarahc 8FTU dilavni na si ` ` sihT") assert.Equal(t, Reverse("The quick bròwn 狐 jumped over the lazy 犬"), "犬 yzal eht revo depmuj 狐 nwòrb kciuq ehT") }
以Stephan202的原始build议为基础,似乎适用于unicodestring:
import "strings"; func Reverse( orig string ) string { var c []string = strings.Split( orig, "", 0 ); for i, j := 0, len(c)-1; i < j; i, j = i+1, j-1 { c[i], c[j] = c[j], c[i] } return strings.Join( c, "" ); }
另外,不使用string包,但不是'unicode-safe':
func Reverse( s string ) string { b := make([]byte, len(s)); var j int = len(s) - 1; for i := 0; i <= j; i++ { b[ji] = s[i] } return string ( b ); }
如果您需要处理字形集群,请使用unicode或regexp模块。
package main import ( "unicode" "regexp" ) func main() { str := "\u0308" + "a\u0308" + "o\u0308" + "u\u0308" println("u\u0308" + "o\u0308" + "a\u0308" + "\u0308" == ReverseGrapheme(str)) println("u\u0308" + "o\u0308" + "a\u0308" + "\u0308" == ReverseGrapheme2(str)) } func ReverseGrapheme(str string) string { buf := []rune("") checked := false index := 0 ret := "" for _, c := range str { if !unicode.Is(unicode.M, c) { if len(buf) > 0 { ret = string(buf) + ret } buf = buf[:0] buf = append(buf, c) if checked == false { checked = true } } else if checked == false { ret = string(append([]rune(""), c)) + ret } else { buf = append(buf, c) } index += 1 } return string(buf) + ret } func ReverseGrapheme2(str string) string { re := regexp.MustCompile("\\PM\\pM*|.") slice := re.FindAllString(str, -1) length := len(slice) ret := "" for i := 0; i < length; i += 1 { ret += slice[length-1-i] } return ret }
看起来有点“迂回”,可能效率不高,但说明了如何使用Reader接口读取string。 使用utf8string时,IntVectors看起来也非常适合作为缓冲区。
当离开“大小”部分时,它会更短,并通过Insert插入到vector中,但是我猜这样会效率不高,因为每次添加新符文时需要将整个vector推回一个。
这个解决scheme肯定与utf8字符。
package main import "container/vector"; import "fmt"; import "utf8"; import "bytes"; import "bufio"; func main() { toReverse := "Smørrebrød"; fmt.Println(toReverse); fmt.Println(reverse(toReverse)); } func reverse(str string) string { size := utf8.RuneCountInString(str); output := vector.NewIntVector(size); input := bufio.NewReader(bytes.NewBufferString(str)); for i := 1; i <= size; i++ { rune, _, _ := input.ReadRune(); output.Set(size - i, rune); } return string(output.Data()); }
这是最快的实现
func Reverse(s string) string { size := len(s) buf := make([]byte, size) for start := 0; start < size; { r, n := utf8.DecodeRuneInString(s[start:]) start += n utf8.EncodeRune(buf[size-start:], r) } return string(buf) } const ( s = "The quick brown 狐 jumped over the lazy 犬" reverse = "犬 yzal eht revo depmuj 狐 nworb kciuq ehT" ) func TestReverse(t *testing.T) { if Reverse(s) != reverse { t.Error(s) } } func BenchmarkReverse(b *testing.B) { for i := 0; i < bN; i++ { Reverse(s) } }
一个我认为在unicode上工作的版本。 它build立在utf8.Rune函数上:
func Reverse(s string) string { b := make([]byte, len(s)); for i, j := len(s)-1, 0; i >= 0; i-- { if utf8.RuneStart(s[i]) { rune, size := utf8.DecodeRuneInString(s[i:len(s)]); utf8.EncodeRune(rune, b[j:j+size]); j += size; } } return string(b); }
符文是一种types,所以使用它。 而且,Go不使用分号。
func reverse(s string) string { l := len(s) m := make([]rune, l) for _, c := range s { l-- m[l] = c } return string(m) } func main() { str := "the quick brown 狐 jumped over the lazy 犬" fmt.Printf("reverse(%s): [%s]\n", str, reverse(str)) }
另一个解决scheme(tm):
package main import "fmt" type Runes []rune func (s Runes) Reverse() (cp Runes) { l := len(s); cp = make(Runes, l) // i <= 1/2 otherwise it will mess up with odd length strings for i := 0; i <= l/2; i++ { cp[i], cp[l-1-i] = s[l-1-i], s[i] } return cp } func (s Runes) String() string { return string(s) } func main() { input := "The quick brown 狐 jumped over the lazy 犬 +odd" r := Runes(input) output := r.Reverse() valid := string(output.Reverse()) == input fmt.Println(len(r), len(output), r, output.Reverse(), valid) }
此代码保留了完整的字符组合序列,并且也应该使用无效的UTF-8input。
package stringutil import "code.google.com/p/go.text/unicode/norm" func Reverse(s string) string { bound := make([]int, 0, len(s) + 1) var iter norm.Iter iter.InitString(norm.NFD, s) bound = append(bound, 0) for !iter.Done() { iter.Next() bound = append(bound, iter.Pos()) } bound = append(bound, len(s)) out := make([]byte, 0, len(s)) for i := len(bound) - 2; i >= 0; i-- { out = append(out, s[bound[i]:bound[i+1]]...) } return string(out) }
如果unicode / norm原语允许在不分配的情况下遍历string的边界,它可能会更有效一些。 另请参阅https://code.google.com/p/go/issues/detail?id=9055 。
尝试下面的代码:
package main import "fmt" func reverse(s string) string { chars := []rune(s) for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 { chars[i], chars[j] = chars[j], chars[i] } return string(chars) } func main() { fmt.Printf("%v\n", reverse("abcdefg")) }
欲了解更多信息,请访问http://golangcookbook.com/chapters/strings/reverse/
和http://www.dotnetperls.com/reverse-string-go
这是另一个解决scheme:
func ReverseStr(s string) string { chars := []rune(s) rev := make([]rune, 0, len(chars)) for i := len(chars) - 1; i >= 0; i-- { rev = append(rev, chars[i]) } return string(rev) }
然而,上面的yazu的解决scheme更加优雅,因为他将[]rune
切换到位。
package reverseString import "strings" // ReverseString - output the reverse string of a given string s func ReverseString(s string) string { strLen := len(s) // The reverse of a empty string is a empty string if strLen == 0 { return s } // Same above if strLen == 1 { return s } // Convert s into unicode points r := []rune(s) // Last index rLen := len(r) - 1 // String new home rev := []string{} for i := rLen; i >= 0; i-- { rev = append(rev, string(r[i])) } return strings.Join(rev, "") }
testing
package reverseString import ( "fmt" "strings" "testing" ) func TestReverseString(t *testing.T) { s := "GO je úžasné!" r := ReverseString(s) fmt.Printf("Input: %s\nOutput: %s", s, r) revR := ReverseString(r) if strings.Compare(s, revR) != 0 { t.Errorf("Expecting: %s\n. Got: %s\n", s, revR) } }
产量
Input: GO je úžasné! Output: !énsažú ej OG PASS ok github.com/alesr/reverse-string 0.098s
这肯定不是最有效的内存解决scheme,但是对于一个“简单的”UTF-8安全解决scheme来说,下面的工作可以完成任务,而不会破坏符文。
在我看来,这是页面上最具可读性和可理解性的。
func reverseStr(str string) (out string) { for _, s := range str { out = string(s) + out } return }
这里是完全不同的,我会说更多的function方法,没有列出其他答案:
func reverse(s string) (ret string) { for _, v := range s { defer func(r rune) { ret += string(r) }(v) } return }
积分
以下两种方法的运行速度比保留组合字符的最快解决scheme快,但这并不是说我在基准testing中缺less某些东西。
//input string s bs := []byte(s) var rs string for len(bs) > 0 { r, size := utf8.DecodeLastRune(bs) rs += fmt.Sprintf("%c", r) bs = bs[:len(bs)-size] } // rs has reversed string
第二种方法受此启发
//input string s bs := []byte(s) cs := make([]byte, len(bs)) b1 := 0 for len(bs) > 0 { r, size := utf8.DecodeLastRune(bs) d := make([]byte, size) _ = utf8.EncodeRune(d, r) b1 += copy(cs[b1:], d) bs = bs[:len(bs) - size] } // cs has reversed bytes
您也可以导入现有的实现:
import "4d63.com/strrev"
然后:
strrev.Reverse("abåd") // returns "dåba"
或者反转一个包含unicode组合字符的string:
strrev.ReverseCombining("abc\u0301\u031dd") // returns "d\u0301\u031dcba"
这些实现支持unicode多字节的正确sorting,并且在反转时支持梳理字符。
注意:许多编程语言中内置的string反转函数不保留组合,识别组合字符需要更多的执行时间。