簡體   English   中英

如何在 Go 中反轉字符串?

[英]How to reverse a string in Go?

我們如何在 Go 中反轉一個簡單的字符串?

在 Go1 中,符文是一種內置類型。

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)
}

在 golang-nuts 郵件列表中Russ Cox建議

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)
}

這是有效的,而無需考慮函數:

func Reverse(s string) (result string) {
  for _,v := range s {
    result = string(v) + result
  }
  return 
}

來自Go 示例項目: golang/example/stringutil/reverse.go ,作者 Andrew Gerrand

/*
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)
}

Go Playground 反轉字符串

反轉字符串“bròwn”后,正確的結果應該是“nwòrb”,而不是“nẁorb”。
注意字母 o 上方的墳墓。


為了保留 Unicode 組合字符,例如“as⃝df̅”與反向結果“f̅ds⃝a”,
請參考下面列出的另一個代碼:

http://rosettacode.org/wiki/Reverse_a_string#Go

通過考慮兩件事,這適用於 unicode 字符串:

  • range通過枚舉 unicode 字符來處理字符串
  • 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);
}

這里的答案太多了。 其中一些是明顯的重復。 但即使從左邊的一個,也很難選擇最好的解決方案。

所以我瀏覽了答案,扔掉了那些不適用於 unicode 的答案,並刪除了重復項。 我對幸存者進行了基准測試以找到最快的。 因此,以下是歸因結果(如果您注意到我遺漏的答案,但值得添加,請隨時修改基准):

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復制它(您不能在那里運行測試)。 重命名並運行go test -bench=.

Simon發布他的解決方案時,我注意到了這個問題,因為字符串是不可變的,所以效率非常低。 其他提議的解決方案也有缺陷; 它們不起作用或效率低下。

這是一個有效的解決方案,除非字符串不是有效的 UTF-8 或字符串包含組合字符。

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 犬")))
}

我編寫了以下尊重 UTF8 編碼和組合字符的Reverse函數:

// 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], j-i)
        } 

        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)
}

我盡我所能使它盡可能高效和可讀。 這個想法很簡單,遍歷符文尋找組合字符,然后就地反轉組合字符的符文。 一旦我們將它們全部覆蓋,將整個字符串的符文也原地反轉。

假設我們想反轉這個字符串bròwn ò由兩個符文表示,一個代表o ,一個代表這個代表“墳墓”的 unicode \́a

為簡單起見,讓我們表示像這個bro'wn的字符串。 我們做的第一件事是尋找組合字符並將它們反轉。 所以現在我們有了字符串br'own 最后,我們反轉整個字符串並以nwo'rb結束。 這是作為nwòrb返回給我們的

如果你想使用它,你可以在這里找到它https://github.com/shomali11/util

這里有一些測試用例來展示幾個不同的場景:

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")
}
//Reverse reverses string using strings.Builder. It's about 3 times faster
//than the one with using a string concatenation
func Reverse(in string) string {
    var sb strings.Builder
    runes := []rune(in)
    for i := len(runes) - 1; 0 <= i; i-- {
        sb.WriteRune(runes[i])
    }
    return sb.String()
}


//Reverse reverses string using string
func Reverse(in string) (out string) {
    for _, r := range in {
        out = string(r) + out
    }
    return
}

BenchmarkReverseStringConcatenation-8   1000000 1571 ns/op  176 B/op    29 allocs/op
BenchmarkReverseStringsBuilder-8        3000000 499 ns/op   56 B/op 6 allocs/op

使用 strings.Builder 比使用字符串連接快大約 3 倍

這是完全不同的,我會說更多功能方法,未在其他答案中列出:

func reverse(s string) (ret string) {
    for _, v := range s {
        defer func(r rune) { ret += string(r) }(v)
    }
    return
}

基於 Stephan202 的原始建議,似乎適用於 unicode 字符串:

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, "" );
}

或者,不使用字符串包,但不使用“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[j-i] = s[i]
    }

    return string ( b );
}

這是最快的實現

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 < b.N; i++ {
        Reverse(s)
    }
}

一個簡單的rune筆畫:

func ReverseString(s string) string {
    runes := []rune(s)
    size := len(runes)
    for i := 0; i < size/2; i++ {
        runes[size-i-1], runes[i] = runes[i],  runes[size-i-1]
    }
    return string(runes)
}

func main() {
    fmt.Println(ReverseString("Abcdefg 漢語 The God"))
}
: doG ehT 語漢 gfedcbA

您還可以導入現有的實現:

import "4d63.com/strrev"

然后:

strrev.Reverse("abåd") // returns "dåba"

或者反轉包含 unicode 組合字符的字符串:

strrev.ReverseCombining("abc\u0301\u031dd") // returns "d\u0301\u031dcba"

這些實現在反轉時支持 unicode 多字節和組合字符的正確排序。

注意:許多編程語言中的內置字符串反轉函數不保留組合,並且識別組合字符需要更多的執行時間。

如果您需要處理字素簇,請使用 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
}

此代碼完整地保留了組合字符的序列,並且也應該適用於無效的 UTF-8 輸入。

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 原語允許在不分配的情況下遍歷字符串的邊界,則效率可能會更高一些。 另請參閱https://code.google.com/p/go/issues/detail?id=9055

這肯定不是最節省內存的解決方案,但對於“簡單”的 UTF-8 安全解決方案,以下內容將完成工作並且不會破壞符文。

在我看來,它是頁面上最易讀和易懂的。

func reverseStr(str string) (out string) {
    for _, s := range str {
        out = string(s) + out
    }

    return
}

注意:這個答案來自 2009 年,所以現在可能有更好的解決方案。


看起來有點“迂回”,可能效率不高,但說明了如何使用 Reader 接口從字符串中讀取。 在處理 utf8 字符串時,IntVectors 似乎也非常適合作為緩沖區。

省略“大小”部分並通過插入插入向量時,它會更短,但我想這會降低效率,因為每次添加新符文時,整個向量都需要推回一個.

此解決方案絕對適用於 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());
}

以下兩種方法比保留組合字符的最快解決方案運行得更快,但這並不是說我在基准測試設置中遺漏了一些東西。

//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
func Reverse(s string) string {
    r := []rune(s)
    var output strings.Builder
    for i := len(r) - 1; i >= 0; i-- {
        output.WriteString(string(r[i]))
    }

    return output.String()
}
func ReverseString(str string) string {
  output :=""
  for _, char := range str {
    output = string(char) + output
  }
  return output
}

// "Luizpa" -> "apziuL"
// "123日本語" -> "語本日321"
// "⚽😎" -> "😎⚽"
// "´a´b´c´" -> "´c´b´a´"

簡單、甜美、高性能

func reverseStr(str string) string {
  strSlice := []rune(str)  //converting to slice of runes
  length := len(strSlice)

  for i := 0; i < (length / 2); i++ {
      strSlice[i], strSlice[length-i-1] = strSlice[length-i-1], strSlice[i]
  }
  return string(strSlice)  //converting back to string
}

按單詞反轉字符串是一個類似的過程。 首先,我們將字符串轉換為字符串數組,其中每個條目都是一個單詞。 接下來,我們將正常的反向循環應用於該數組。 最后,我們將結果合並成一個字符串,我們可以返回給調用者。

package main

import (
    "fmt"
    "strings"
)

func reverse_words(s string) string {
    words := strings.Fields(s)
    for i, j := 0, len(words)-1; i < j; i, j = i+1, j-1 {
        words[i], words[j] = words[j], words[i]
    }
    return strings.Join(words, " ")
}

func main() {
    fmt.Println(reverse_words("one two three"))
}

符文是一種類型,所以使用它。 此外,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))
}

我認為適用於 unicode 的版本。 它建立在 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);
}

試試下面的代碼:

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

對於簡單的字符串,可以使用這樣的結構:

func Reverse(str string) string {
    if str != "" {
        return Reverse(str[1:]) + str[:1]
    }
    return ""   
}
func reverseStr(b string) {
for _, v := range []rune(b) {
    defer fmt.Printf("%c", v)

}
 }

Defer 對此很有用,因為它是 LIFO - 后進先出

字符串在 golang 中是不可變的對象,與 C 不同,golang 不可能實現反向。 使用 C ,你可以做類似的事情,

void reverseString(char *str) {
  int length = strlen(str)
  for(int i = 0, j = length-1; i < length/2; i++, j--)
  {
    char tmp = str[i];
    str[i] = str[j];
    str[j] = tmp;
  }
}

但是對於golang,接下來是先使用字節將輸入轉換為字節,然后在反轉后反轉字節數組,在返回之前轉換回字符串。 僅適用於非 unicode 類型字符串。

package main

import "fmt"

func main() {
    s := "test123 4"
    fmt.Println(reverseString(s))
}

func reverseString(s string) string {
    a := []byte(s)
    for i, j := 0, len(s)-1; i < j; i++ {
        a[i], a[j] = a[j], a[i]
        j--
    }
    return string(a)
}

這是另一個解決方案:

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 上面的解決方案更優雅,因為他將[]rune切片反轉到位。

另一個解決方案(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)
}
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, "")
}

測試

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
    func reverseString(someString string) string {
        runeString := []rune(someString)
        var reverseString string
        for i := len(runeString)-1; i >= 0; i -- {
            reverseString += string(runeString[i])
        }
        return reverseString
    }

另一個技巧是使用內置語言功能,例如defer

package main

import "fmt"

func main() {
    var name string
    fmt.Scanln(&name)

    for _, char := range []rune(name) {
        defer fmt.Printf("%c", char) // <-- LIFO does it all for you
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM