Как мы можем изменить простую строку в Go?
Как изменить строку в Go?
Ответ 1
В 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)
}
Ответ 2
Russ Cox, в списке рассылки golang-nuts, предлагает
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)
}
Ответ 3
Это работает без всяких проблем с функциями:
func Reverse(s string) (result string) {
for _,v := range s {
result = string(v) + result
}
return
}
Ответ 4
Это работает в строках unicode, рассматривая 2 вещи:
- диапазон работает с строкой, перечисляя символы Unicode. Строка
- может быть построена из int slices, где каждый элемент является символом Unicode.
Итак, вот оно:
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);
}
Ответ 5
Из Перейти пример проектов: golang/example/stringutil/reverse.go, 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)
}
Перейти в Playground для изменения строки
После изменения строки "bròwn" правильный результат должен быть "nwòrb", а не "nẁorb".
Обратите внимание на могилу над буквой o.
Для сохранения Unicode, объединяющего символы, такие как "as⃝df̅" с обратным результатом "f̅ds⃝a",
обратитесь к следующему коду, указанному ниже:
Ответ 6
Я заметил этот вопрос, когда Simon разместил его решение, которое, поскольку строки неизменяемы, очень неэффективен. Другие предлагаемые решения также ошибочны; они не работают или неэффективны.
Здесь работает эффективное решение, за исключением случаев, когда строка недопустима. 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 犬")))
}
Ответ 7
Здесь слишком много ответов. Некоторые из них являются четкими дубликатами. Но даже с левой стороны трудно выбрать лучшее решение.
Итак, я прошел ответы, выбросил тот, который не работает для unicode, а также удалил дубликаты. Я сравнивал выживших, чтобы найти самое быстрое. Итак, приведены результаты с атрибуцией (если вы заметите ответы, которые я пропустил, но стоит добавить, не стесняйтесь изменять эталон):
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=.
Ответ 8
Я написал следующую функцию 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
и один для этого unicode \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")
}
Ответ 9
Основываясь на оригинальном предложении 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, "" );
}
Альтернативный, не используя пакет строк, но не '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 );
}
Ответ 10
Это самая быстрая реализация
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)
}
}
Ответ 11
Если вам нужно обработать кластеры grapheme, используйте модуль 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
}
Ответ 12
Этот код сохраняет последовательности комбинаций символов неповрежденными и также должен работать с недопустимым входом 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)
}
Это может быть немного более эффективным, если примитивы unicode/norm допускает итерацию через границы строки без выделения. См. Также https://code.google.com/p/go/issues/detail?id=9055.
Ответ 13
Здесь совсем другое, я бы сказал, более функциональный подход, не перечисленный среди других ответов:
func reverse(s string) (ret string) {
for _, v := range s {
defer func(r rune) { ret += string(r) }(v)
}
return
}
Ответ 14
Вы также можете импортировать существующую реализацию:
import "4d63.com/strrev"
Тогда:
strrev.Reverse("abåd") // returns "dåba"
Или изменить строку, включая символы кодирования в Юникоде:
strrev.ReverseCombining("abc\u0301\u031dd") // returns "d\u0301\u031dcba"
Эти реализации поддерживают правильный порядок юникодовых многобайтовых и расчесывающих символов при обратном.
Примечание. Встроенные строковые обратные функции во многих языках программирования не сохраняют объединение, а идентификация комбинированных символов требует значительно большего времени выполнения.
Ответ 15
ПРИМЕЧАНИЕ: Этот ответ получен в 2009 году, так что, вероятно, к настоящему времени существуют более эффективные решения.
Выглядит немного "окольными" и, вероятно, не очень эффективно, но иллюстрирует, как интерфейс Reader может использоваться для чтения из строк. IntVectors также кажутся очень подходящими в качестве буферов при работе со строками utf8.
Это было бы еще короче, если вы пропустите часть 'size' и вставку в вектор с помощью 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());
}
Ответ 16
rune - это тип, поэтому используйте его. Более того, 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))
}
Ответ 17
Это, безусловно, не самое эффективное решение для памяти, но для "простого" безопасного решения UTF-8 следующее будет выполняться, а не прерывать руны.
Это, на мой взгляд, наиболее читаемое и понятное на странице.
func reverseStr(str string) (out string) {
for _, s := range str {
out = string(s) + out
}
return
}
Ответ 18
Следующие два метода работают быстрее, чем быстрое решение, которое поддерживает объединение символов, хотя это не означает, что я что-то пропускаю в своей тестовой настройке.
//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
Ответ 19
Версия, которая, я думаю, работает в unicode. Он построен на функциях 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);
}
Ответ 20
попробуйте под кодом:
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
Ответ 21
Для простых строк можно использовать такую конструкцию:
func Reverse(str string) string {
if str != "" {
return Reverse(str[1:]) + str[:1]
}
return ""
}
Ответ 22
Вот еще одно решение:
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)
}
Однако решение yazu выше более элегантно, так как он реверсирует срез []rune
на месте.
Ответ 23
Еще одно решение (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)
}
Ответ 24
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
Ответ 25
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
}
Ответ 26
//Reverse reverses string using strings.Builder. It 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 раза быстрее, чем использование конкатенации строк