¿Cómo podemos invertir una cadena simple en Go?
¿Cómo podemos invertir una cadena simple en Go?
Respuestas:
En Go1, la runa es un tipo incorporado.
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, en la lista de correo de golang-nuts , sugiere
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)
?
Esto funciona, sin todos los problemas con las funciones:
func Reverse(s string) (result string) {
for _,v := range s {
result = string(v) + result
}
return
}
Esto funciona en cadenas Unicode considerando 2 cosas:
Así que aquí va:
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
y luego doblaría el for en una sola línea for _, c:=range s { o[i--]=c; }
. Hombre, ODIO el por sin paréntesis - ¿está permitido?for(_, c:=range s) { o[i--]=c; }
De proyectos de ejemplo de Go: golang / example / stringutil / reverse.go , por 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 para revertir una cuerda
Después de invertir la cadena "bròwn", el resultado correcto debería ser "nwòrb", no "nẁorb".
Tenga en cuenta la tumba sobre la letra o.
Para conservar caracteres de combinación Unicode como "as⃝df̅" con el resultado inverso "f̅ds⃝a",
consulte otro código que se enumera a continuación:
Noté esta pregunta cuando Simon publicó su solución que, dado que las cadenas son inmutables, es muy ineficiente. Las otras soluciones propuestas también son defectuosas; no funcionan o son ineficientes.
Esta es una solución eficiente que funciona, excepto cuando la cadena no es UTF-8 válida o la cadena contiene 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)
no funciona en todos los casos.
Aquí hay demasiadas respuestas. Algunos de ellos son claros duplicados. Pero incluso desde la izquierda, es difícil seleccionar la mejor solución.
Así que revisé las respuestas, tiré la que no funciona para Unicode y también eliminé los duplicados. Evalué a los supervivientes para encontrar el más rápido. Entonces, aquí están los resultados con atribución (si nota las respuestas que me perdí, pero que vale la pena agregar, no dude en modificar el punto de referencia):
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
Así que aquí está el método más 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 alguna razón, no puedo agregar un punto de referencia, por lo que puede copiarlo PlayGround(no puede ejecutar pruebas allí). Cambiar el nombre y ejecutargo test -bench=.
Escribí la siguiente Reverse
función que respeta la codificación UTF8 y los 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)
}
Hice todo lo posible para que fuera lo más eficiente y legible posible. La idea es simple, atraviesa las runas en busca de personajes combinados y luego invierte las runas de los personajes combinados en su lugar. Una vez que los hayamos cubierto todos, invierta las runas de toda la cuerda también en su lugar.
Digamos que nos gustaría invertir esta cadena bròwn
. El ò
está representado por dos runas, una para el o
y otra para este unicode \u0301a
que representa la "tumba".
Para simplificar, representemos la cadena de esta manera bro'wn
. Lo primero que hacemos es buscar caracteres combinados y revertirlos. Entonces ahora tenemos la cadena br'own
. Finalmente, invertimos toda la cadena y terminamos con nwo'rb
. Esto se nos devuelve comonwòrb
Puede encontrarlo aquí https://github.com/shomali11/util si desea utilizarlo.
A continuación, se muestran algunos casos de prueba para mostrar un par de escenarios 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")
}
Se basa en la sugerencia original de Stephan202 y parece funcionar para cadenas 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, sin usar el paquete de cadenas, pero no '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 es aproximadamente 3 veces más rápido que usar la concatenación de cadenas
Aquí es bastante diferente, diría que un enfoque más funcional, que no figura entre otras respuestas:
func reverse(s string) (ret string) {
for _, v := range s {
defer func(r rune) { ret += string(r) }(v)
}
return
}
ret
se mantiene cerrada para su procesamiento posterior, por cada función de aplazamiento.
Esta es la implementación más 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 conserva intactas las secuencias de combinación de caracteres y también debería funcionar con entradas UTF-8 no válidas.
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)
}
Podría ser un poco más eficiente si las primitivas unicode / norm permitieran iterar a través de los límites de una cadena sin asignar. Consulte también https://code.google.com/p/go/issues/detail?id=9055 .
[]byte
a string
Go, se reemplaza la "entrada UTF-8 no válida" por un punto de código válido \uFFFD
.
string
no existe. Pero puede existir en a []byte
.
Si necesita manejar grupos de grafemas, use el módulo unicode o 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
se cotiza la salida, modifica la cotización inicial.
También puede importar una implementación existente:
import "4d63.com/strrev"
Luego:
strrev.Reverse("abåd") // returns "dåba"
O para invertir una cadena que incluya caracteres de combinación Unicode:
strrev.ReverseCombining("abc\u0301\u031dd") // returns "d\u0301\u031dcba"
Estas implementaciones admiten el orden correcto de unicode multibyte y la combinación de caracteres cuando se invierte.
Nota: Las funciones de inversión de cadena incorporadas en muchos lenguajes de programación no conservan la combinación, y la identificación de caracteres combinados requiere mucho más tiempo de ejecución.
Seguramente no es la solución más eficiente en memoria, pero para una solución segura UTF-8 "simple", lo siguiente hará el trabajo y no romperá las runas.
En mi opinión, es el más legible y comprensible de la página.
func reverseStr(str string) (out string) {
for _, s := range str {
out = string(s) + out
}
return
}
Los siguientes dos métodos se ejecutan más rápido que la solución más rápida que conserva la combinación de caracteres , aunque eso no quiere decir que me esté perdiendo algo en mi configuración de referencia.
//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 en este
//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 respuesta es de 2009, por lo que probablemente ya existen mejores soluciones.
Parece un poco "indirecto" y probablemente no muy eficiente, pero ilustra cómo se puede usar la interfaz de Reader para leer cadenas. Los IntVectors también parecen muy adecuados como búfer cuando se trabaja con cadenas utf8.
Sería aún más corto si se omite la parte de 'tamaño' y la inserción en el vector mediante Insertar, pero supongo que sería menos eficiente, ya que todo el vector debe retroceder uno cada vez que se agrega una nueva runa. .
Esta solución definitivamente funciona con 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());
}
Una versión que creo que funciona en Unicode. Se basa en las funciones 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 es un tipo, así que úsala. Además, Go no usa punto y coma.
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))
}
prueba el siguiente código:
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 obtener más información, consulte http://golangcookbook.com/chapters/strings/reverse/
y http://www.dotnetperls.com/reverse-string-go
Para cadenas simples, es posible utilizar dicha construcción:
func Reverse(str string) string {
if str != "" {
return Reverse(str[1:]) + str[:1]
}
return ""
}
Aquí hay otra solución:
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)
}
Sin embargo, la solución de yazu anterior es más elegante ya que invierte el []rune
corte en su lugar.
Otra solución más (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, "")
}
Prueba
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)
}
}
Salida
Input: GO je úžasné!
Output: !énsažú ej OG
PASS
ok github.com/alesr/reverse-string 0.098s
a+´
lugar deá
. Me pregunto cómo se podría tener eso en cuenta, sin normalizarlo.