Как мы можем перевернуть простую строку в 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-nut предлагает
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)
?
Это работает, без всякой возни с функциями:
func Reverse(s string) (result string) {
for _,v := range s {
result = string(v) + result
}
return
}
Это работает со строками Unicode, учитывая 2 вещи:
Итак, вот оно:
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
а затем свернул for в одну строку for _, c:=range s { o[i--]=c; }
. Человек, которого я НЕНАВИЖУ, без круглых скобок - это разрешено:for(_, c:=range s) { o[i--]=c; }
Примеры проектов из Go: golang / example / stringutil / reverse.go , Эндрю Герранд
/*
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)
}
Перейти на площадку для переворота струны
После изменения строки «bròwn» правильным результатом должно быть «nwòrb», а не «nẁorb».
Обратите внимание на могилу над буквой o.
Для сохранения Unicode-сочетания символов, таких как «as⃝df̅» с обратным результатом «f̅ds⃝a»,
обратитесь к другому коду, указанному ниже:
Я заметил этот вопрос, когда Саймон опубликовал свое решение, которое, поскольку строки неизменяемы, очень неэффективно. Другие предлагаемые решения также ошибочны; они не работают или неэффективны.
Вот эффективное решение, которое работает, за исключением случаев, когда строка не является допустимой 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 犬")))
}
return string(runes)
работает не во всех случаях.
Здесь слишком много ответов. Некоторые из них - явные дубликаты. Но даже из левого варианта сложно выбрать лучшее решение.
Я просмотрел ответы, выбросил тот, который не работает для юникода, а также удалил дубликаты. Я сравнил выживших, чтобы найти самых быстрых. Итак, вот результаты с атрибуцией (если вы заметили ответы, которые я пропустил, но которые стоит добавить, не стесняйтесь изменять тест):
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=.
Я написал следующую Reverse
функцию, которая учитывает кодировку UTF8 и комбинированные символы:
// 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
и один для этого юникода \u0301a
, представляемого «могила».
Для простоты представим строку вот так 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
}
ret
закрывается для дальнейшей обработки каждой функцией отсрочки.
Основываясь на исходном предложении 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, "" );
}
Альтернативный вариант, без использования строкового пакета, но не «безопасный для юникода»:
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)
}
}
Этот код сохраняет последовательности комбинирования символов без изменений и также должен работать с недопустимым вводом 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)
}
Было бы немного эффективнее, если бы примитивы юникода / нормы позволяли выполнять итерацию через границы строки без выделения. См. Также https://code.google.com/p/go/issues/detail?id=9055 .
[]byte
в string
Go заменяет «недопустимый ввод UTF-8» на допустимый код \uFFFD
.
string
не существует. Но он может существовать в []byte
.
Если вам нужно обрабатывать кластеры графем, используйте модуль 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
}
str
выводится в кавычках, это изменяет начальную цитату!
Вы также можете импортировать существующую реализацию:
import "4d63.com/strrev"
Затем:
strrev.Reverse("abåd") // returns "dåba"
Или перевернуть строку, содержащую объединяющие символы Unicode:
strrev.ReverseCombining("abc\u0301\u031dd") // returns "d\u0301\u031dcba"
Эти реализации поддерживают правильный порядок многобайтовых символов Юникода и расчесывание символов в обратном порядке.
Примечание. Встроенные функции реверса строк во многих языках программирования не сохраняют комбинирование, а идентификация комбинируемых символов требует значительно большего времени выполнения.
Конечно, это не самое эффективное решение с точки зрения памяти, но для «простого» безопасного решения UTF-8 следующее выполнит свою работу и не сломает руны.
На мой взгляд он самый читаемый и понятный на странице.
func reverseStr(str string) (out string) {
for _, s := range str {
out = string(s) + out
}
return
}
Следующие два метода работают быстрее, чем самое быстрое решение, сохраняющее комбинирование символов , хотя это не значит, что мне чего-то не хватает в моей настройке теста.
//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
ПРИМЕЧАНИЕ. Этот ответ относится к 2009 году, поэтому, вероятно, на данный момент существуют лучшие решения.
Выглядит немного «окольным путем» и, вероятно, не очень эффективно, но иллюстрирует, как интерфейс Reader может использоваться для чтения из строк. IntVectors также кажутся очень подходящими в качестве буферов при работе со строками utf8.
Это было бы еще короче, если бы не было части `` размер '' и вставка в вектор с помощью Insert, но я предполагаю, что это будет менее эффективно, так как весь вектор тогда нужно отодвигать на единицу каждый раз, когда добавляется новая руна. .
Это решение определенно работает с символами 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());
}
Версия, которая, как мне кажется, работает в Юникоде. Он построен на функциях 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);
}
руна - это тип, поэтому используйте ее. Более того, 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))
}
попробуйте ниже код:
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 ""
}
Простой штрих с 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
Вот еще одно решение:
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)
}
Однако вышеприведенное решение Язу более элегантно, поскольку он переворачивает []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
a+´
вместоá
. Интересно, как это можно было учесть без нормализации.