web-dev-qa-db-fra.com

Comment inverser une chaîne dans Go?

Comment pouvons-nous inverser une chaîne simple dans Go?

79
user211499

Dans Go1, Rune est un type intégré.

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

Russ Cox, sur la liste de diffusion golang-nuts , suggère

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

Cela fonctionne, sans tout le foutoir avec les fonctions:

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

Cela fonctionne sur les chaînes unicode en considérant 2 choses:

  • range fonctionne sur une chaîne en énumérant des caractères unicode
  • la chaîne peut être construite à partir d'intersections où chaque élément est un caractère Unicode.

Alors voilà:

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

De Aller aux exemples de projets: golang/exemple/stringutil/reverse.go , par 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 pour inverser une chaîne

Après avoir inversé la chaîne "bròwn", le résultat correct doit être "nwòrb" et non "nẁorb".
Notez la sépulture au-dessus de la lettre o.


Pour conserver Unicode combinant des caractères tels que "as⃝df̅" avec le résultat inverse "f̅ds⃝a",
s'il vous plaît se référer à un autre code énuméré ci-dessous: 

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

11
Ivan Chau

J'ai remarqué cette question lorsque Simon posted sa solution qui, comme les chaînes sont immuables, est très inefficace. Les autres solutions proposées sont également défectueuses. ils ne travaillent pas ou ils sont inefficaces.

Voici une solution efficace qui fonctionne, sauf lorsque la chaîne n'est pas valide UTF-8 ou que la chaîne contient des caractères de combinaison.

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

Il y a trop de réponses ici. Certains d'entre eux sont des doublons clairs. Mais même à gauche, il est difficile de choisir la meilleure solution.

J'ai donc parcouru les réponses, jeté celle qui ne fonctionne pas pour Unicode et supprimé les doublons. J'ai comparé les survivants pour trouver le plus rapide. Donc voici les résultats avec attribution (si vous remarquez les réponses que j'ai manquées, mais que vous pouvez ajouter, n'hésitez pas à modifier le point de repère):

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

Donc voici la méthode la plus rapide de 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)
}

Pour une raison quelconque, je ne peux pas ajouter de repère, vous pouvez donc le copier à partir de PlayGround (vous ne pouvez pas y exécuter de tests). Renommez-le et lancez go test -bench=.

6
Salvador Dali

J'ai écrit la fonction Reverse suivante qui respecte le codage UTF8 et les caractères combinés:

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

J'ai fait de mon mieux pour le rendre aussi efficace et lisible que possible. L'idée est simple: parcourez les runes à la recherche de caractères combinés, puis inversez-les en place. Une fois que nous avons tout couvert, inversez les runes de la chaîne entière également en place.

Disons que nous aimerions inverser cette chaîne bròwn. Le ò est représenté par deux runes, une pour la o et une pour ce unicode \u0301a qui représente la "tombe".

Pour plus de simplicité, représentons la chaîne comme ceci bro'wn. La première chose à faire est de rechercher les caractères combinés et de les inverser. Nous avons maintenant la chaîne br'own. Enfin, nous inversons la chaîne entière et finissons par nwo'rb. Ceci nous est retourné sous la forme nwòrb

Vous pouvez le trouver ici https://github.com/shomali11/util si vous souhaitez l’utiliser.

Voici quelques cas de test pour illustrer deux scénarios différents:

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")
}
5
Raed Shomali

Si vous devez gérer des grappes de graphèmes, utilisez le module unicode ou 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
}
3
masakielastic

S'appuyant sur la suggestion initiale de Stephan202, et semble fonctionner pour les chaînes 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, "" );
}

Autre solution, n'utilisez pas de paquet de chaînes, mais pas '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 );
}
3
martin clayton

C'est l'implémentation la plus rapide

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

Vous pouvez également importer une implémentation existante:

import "4d63.com/strrev"

Ensuite:

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

Ou pour inverser une chaîne comprenant des caractères de combinaison Unicode:

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

Ces implémentations prennent en charge le tri correct des caractères multi-octets et de combinaison de caractères unicode lorsqu’ils sont inversés.

Remarque: Les fonctions d'inversion de chaîne intégrées à de nombreux langages de programmation ne préservent pas la combinaison, et l'identification de la combinaison de caractères nécessite un temps d'exécution considérablement plus long.

2
Leigh McCulloch

Cela semble un peu «rond-point», et probablement pas très efficace, mais illustre comment l’interface Reader peut être utilisée pour lire des chaînes. IntVectors semble également très approprié comme tampon lorsque vous travaillez avec des chaînes utf8.

Il serait encore plus court en laissant de côté la partie 'taille' et l'insertion dans le vecteur par Insert, mais j'imagine que ce serait moins efficace, car le vecteur entier doit alors être repoussé de un à chaque nouvelle rangée .

Cette solution fonctionne définitivement avec les caractères 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());
}
2
Oliver Mason

Ce n’est assurément pas la solution la plus efficace en termes de mémoire, mais pour une solution "simple" sans danger pour UTF-8, les tâches suivantes seront accomplies et ne casseront pas les runes.

C'est à mon avis le plus lisible et le plus compréhensible de la page.

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

    return
}
2
donatJ

Les deux méthodes suivantes fonctionnent plus rapidement que la solution la plus rapide qui conserve la combinaison de caractères , même si cela ne veut pas dire que quelque chose me manque dans ma configuration de référence. 

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

Deuxième méthode inspirée par 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
1
Sridhar

Ce code conserve intactes les séquences de combinaison de caractères, et .__ devrait également fonctionner avec une entrée UTF-8 non valide.

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

Cela pourrait être un peu plus efficace si les primitives unicode/norme Permettaient de parcourir les limites d’une chaîne sans Voir aussi https://code.google.com/p/go/issues/detail?id=9055 .

1
rog

Pour des chaînes simples, il est possible d'utiliser une telle construction:

func Reverse(str string) string {
    if str != "" {
        return Reverse(str[1:]) + str[:1]
    }
    return ""   
}
1
Sergo Kurbanov

essayez le code ci-dessous:

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

pour plus d'informations, consultez http://golangcookbook.com/chapters/strings/reverse/
et http://www.dotnetperls.com/reverse-string-go

1
Bunker Boy

rune est un type, utilisez-le. De plus, Go n'utilise pas de points-virgules.

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

Une version qui, à mon avis, fonctionne sur Unicode. Il est construit sur les fonctions 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);
}
1
Jonathan Wright

Voici une approche très différente, je dirais plus fonctionnelle, qui ne figure pas parmi les autres réponses

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

Crédits

1
Vladimir Bauer
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, "")
}

Test

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

Sortie

Input: GO je úžasné!
Output: !énsažú ej OG
PASS
ok      github.com/alesr/reverse-string 0.098s
0
Alessandro Resta

Encore une autre solution (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)
}
0
OneOfOne
    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
    }
0
saurabh

Voici encore une autre solution:

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

Cependant, la solution ci-dessus de Yazu est plus élégante car il inverse la tranche []rune en place.

0
Jabba