How to reverse a string in Go?

前端 未结 28 1893
天涯浪人
天涯浪人 2020-12-04 06:45

How can we reverse a simple string in Go?

相关标签:
28条回答
  • 2020-12-04 07:25

    There are too many answers here. Some of them are clear duplicates. But even from the left one, it is hard to select the best solution.

    So I went through the answers, thrown away the one that does not work for unicode and also removed duplicates. I benchmarked the survivors to find the fastest. So here are the results with attribution (if you notice the answers that I missed, but worth adding, feel free to modify the benchmark):

    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
    

    So here is the fastest method by 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)
    }
    

    For some reason I can't add a benchmark, so you can copy it from PlayGround (you can't run tests there). Rename it and run go test -bench=.

    0 讨论(0)
  • 2020-12-04 07:25

    This is the fastest implementation

    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)
        }
    }
    
    0 讨论(0)
  • 2020-12-04 07:26

    This works on unicode strings by considering 2 things:

    • range works on string by enumerating unicode characters
    • string can be constructed from int slices where each element is a unicode character.

    So here it goes:

    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);
    }
    
    0 讨论(0)
  • 2020-12-04 07:26

    rune is a type, so use it. Moreover, Go doesn't use semicolons.

    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))
    }
    
    0 讨论(0)
  • 2020-12-04 07:27

    The following two methods run faster than the fastest solution that preserve combining characters, though that's not to say I'm missing something in my benchmark setup.

    //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
    

    Second method inspired by this

    //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
    
    0 讨论(0)
  • 2020-12-04 07:28

    I wrote the following Reverse function which respects UTF8 encoding and combined characters:

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

    I did my best to make it as efficient and readable as possible. The idea is simple, traverse through the runes looking for combined characters then reverse the combined characters' runes in-place. Once we have covered them all, reverse the runes of the entire string also in-place.

    Say we would like to reverse this string bròwn. The ò is represented by two runes, one for the o and one for this unicode \u0301a that represents the "grave".

    For simplicity, let's represent the string like this bro'wn. The first thing we do is look for combined characters and reverse them. So now we have the string br'own. Finally, we reverse the entire string and end up with nwo'rb. This is returned to us as nwòrb

    You can find it here https://github.com/shomali11/util if you would like to use it.

    Here are some test cases to show a couple of different scenarios:

    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("                                                                    
    0 讨论(0)
提交回复
热议问题