Como podemos reverter uma string simples em Go?
Como podemos reverter uma string simples em Go?
Respostas:
Em Go1, a runa é um tipo embutido.
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)
}
Russ Cox, na lista de discussão golang-nuts , sugere
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)
}
rune:=[]rune(input)
?
Isso funciona, sem toda a confusão com as funções:
func Reverse(s string) (result string) {
for _,v := range s {
result = string(v) + result
}
return
}
Isso funciona em strings Unicode considerando 2 coisas:
Então aqui vai:
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);
}
i:=len(o)-1
e dobraria o for em uma única linha for _, c:=range s { o[i--]=c; }
. Cara, eu ODEIO o sem parênteses - isso é permitido:for(_, c:=range s) { o[i--]=c; }
De projetos de exemplo Go: golang / example / stringutil / reverse.go , de 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)
}
Vá ao Playground para inverter uma corda
Depois de inverter a string "bròwn", o resultado correto deve ser "nwòrb", não "nẁorb".
Observe a sepultura acima da letra o.
Para preservar caracteres de combinação Unicode como "as⃝df̅" com o resultado reverso "f̅ds⃝a",
consulte outro código listado abaixo:
Percebi essa pergunta quando Simon postou sua solução que, como as strings são imutáveis, é muito ineficiente. As outras soluções propostas também são falhas; eles não funcionam ou são ineficientes.
Esta é uma solução eficiente que funciona, exceto quando a string não é UTF-8 válida ou a string contém caracteres combinados.
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 犬")))
}
return string(runes)
não funciona para todos os casos.
Existem muitas respostas aqui. Alguns deles são duplicatas claras. Mas, mesmo da esquerda, é difícil selecionar a melhor solução.
Então eu fui pelas respostas, joguei fora a que não funciona para Unicode e também removi as duplicatas. Eu comparei os sobreviventes para encontrar o mais rápido. Então, aqui estão os resultados com atribuição (se você notar as respostas que perdi, mas vale a pena adicionar, sinta-se à vontade para modificar o 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
Então, aqui está o método mais rápido 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)
}
Por alguma razão, não consigo adicionar um benchmark, então você pode copiá-lo PlayGround(você não pode executar testes lá). Renomeie-o e executego test -bench=.
Escrevi a seguinte Reverse
função que respeita a codificação UTF8 e os caracteres combinados:
// 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)
}
Fiz o meu melhor para torná-lo o mais eficiente e legível possível. A ideia é simples, atravesse as runas procurando por caracteres combinados e então inverta as runas dos personagens combinados no local. Depois de cobrir todos eles, inverta as runas de toda a corda no lugar.
Digamos que gostaríamos de reverter essa string bròwn
. O ò
é representado por duas runas, uma para o o
e outra para este unicode \u0301a
que representa o "túmulo".
Para simplificar, vamos representar a string desta forma bro'wn
. A primeira coisa que fazemos é procurar caracteres combinados e invertê-los. Portanto, agora temos a corda br'own
. Finalmente, invertemos toda a string e terminamos com nwo'rb
. Isso é devolvido a nós comonwòrb
Você pode encontrá-lo aqui https://github.com/shomali11/util se desejar usá-lo.
Aqui estão alguns casos de teste para mostrar alguns cenários diferentes:
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")
}
Com base na sugestão original de Stephan202 e parece funcionar para strings 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, "" );
}
Alternativo, não usando o pacote de strings, mas não 'seguro para Unicode':
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 );
}
//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
Usar strings.Builder é cerca de 3 vezes mais rápido do que usar concatenação de string
Aqui é bem diferente, eu diria uma abordagem mais funcional, não listada entre outras respostas:
func reverse(s string) (ret string) {
for _, v := range s {
defer func(r rune) { ret += string(r) }(v)
}
return
}
ret
é mantida em fechamento para processamento posterior, por cada função defer.
Esta é a implementação mais rápida
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)
}
}
Este código preserva as sequências de caracteres combinados intactas e também deve funcionar com entrada UTF-8 inválida.
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)
}
Poderia ser um pouco mais eficiente se as primitivas unicode / norm permitissem a iteração pelos limites de uma string sem alocação. Consulte também https://code.google.com/p/go/issues/detail?id=9055 .
[]byte
para string
Go substitui "entrada UTF-8 inválida" por um ponto de código válido \uFFFD
.
string
não existe. Mas pode existir em um []byte
.
Se você precisar lidar com clusters de grafemas, use o módulo 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
}
str
saída for citada, ela modifica a citação inicial!
Você também pode importar uma implementação existente:
import "4d63.com/strrev"
Então:
strrev.Reverse("abåd") // returns "dåba"
Ou para inverter uma string incluindo caracteres de combinação Unicode:
strrev.ReverseCombining("abc\u0301\u031dd") // returns "d\u0301\u031dcba"
Essas implementações suportam a ordenação correta de caracteres multibyte Unicode e combing quando invertidos.
Nota: As funções reversas de string integradas em muitas linguagens de programação não preservam a combinação, e a identificação de caracteres combinados requer muito mais tempo de execução.
Certamente não é a solução mais eficiente em termos de memória, mas para uma solução segura UTF-8 "simples", o seguinte fará o trabalho e não quebrará runas.
Na minha opinião, é o mais legível e compreensível da página.
func reverseStr(str string) (out string) {
for _, s := range str {
out = string(s) + out
}
return
}
Os dois métodos a seguir são executados mais rapidamente do que a solução mais rápida que preserva a combinação de caracteres , embora isso não queira dizer que esteja faltando algo em minha configuração de benchmark.
//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
Segundo método inspirado neste
//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
NOTA: Esta resposta é de 2009, então provavelmente existem soluções melhores por aí agora.
Parece um pouco "indireto" e provavelmente não é muito eficiente, mas ilustra como a interface do Reader pode ser usada para ler strings. IntVectors também parecem muito adequados como buffers ao trabalhar com strings utf8.
Seria ainda mais curto deixando de fora a parte do 'tamanho' e a inserção no vetor por Insert, mas acho que seria menos eficiente, pois o vetor inteiro precisa ser empurrado para trás em um cada vez que uma nova runa é adicionada .
Esta solução definitivamente funciona com caracteres 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());
}
Uma versão que acho que funciona em Unicode. Ele é baseado nas funções 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);
}
runa é um tipo, então use-a. Além disso, Go não usa ponto-e-vírgula.
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))
}
tente o código abaixo:
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"))
}
para obter mais informações, verifique http://golangcookbook.com/chapters/strings/reverse/
e http://www.dotnetperls.com/reverse-string-go
Para strings simples, é possível usar essa construção:
func Reverse(str string) string {
if str != "" {
return Reverse(str[1:]) + str[:1]
}
return ""
}
Aqui está outra solução:
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)
}
No entanto, a solução de yazu acima é mais elegante, pois ele inverte a []rune
fatia no lugar.
Ainda outra solução (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, "")
}
Teste
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)
}
}
Resultado
Input: GO je úžasné!
Output: !énsažú ej OG
PASS
ok github.com/alesr/reverse-string 0.098s
a+´
vez deá
. Eu me pergunto como isso poderia ser levado em consideração, sem normalizá-lo.