Як можна повернути простий рядок у 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)
}
Расс Кокс із списку розсилки голанг-горіхів пропонує
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 _, c:=range s { o[i--]=c; }
. Людина, яку я ненавиджу за без дужок - це дозволено:for(_, c:=range s) { o[i--]=c; }
З Go прикладів проектів: golang / наприклад / StringUtil / reverse.go , Ендрю 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)
}
Перейдіть на майданчик для зворотного струни
Після обертання рядка "bròwn" правильним результатом має бути "nwòrb", а не "nẁorb".
Зверніть увагу на могилу над буквою о.
Для збереження 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)
працює не у всіх випадках.
Тут занадто багато відповідей. Деякі з них - це чіткі дублікати. Але навіть з лівого боку важко вибрати найкраще рішення.
Тому я переглянув відповіді, викинув той, який не працює для 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=.
Я написав таку 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")
}
На основі оригінальної пропозиції 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 );
}
//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
зберігається в закритому режимі для подальшої обробки кожною функцією відкладання.
Це найшвидша реалізація
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)
}
Це може бути трохи ефективніше, якби примітиви unicode / norm дозволили повторюватись через межі рядка без виділення. Дивіться також https://code.google.com/p/go/isissue/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"
Або скасувати рядок, включаючи символи, що поєднують унікод:
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());
}
Версія, на яку я думаю, працює на 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);
}
руна - це тип, тому використовуйте її. Більше того, 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 ""
}
Ось ще одне рішення:
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
шматочок на місці.
Ще одне рішення (тм):
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+´
замістьá
. Цікаво, як це можна було б врахувати, не нормалізуючи це.