Как перевернуть строку в Go? - PullRequest
83 голосов
/ 18 ноября 2009

Как мы можем перевернуть простую строку в Go?

Ответы [ 25 ]

77 голосов
/ 05 апреля 2012

В 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)
}
49 голосов
/ 18 ноября 2009

Расс Кокс, в списке рассылки 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)
}
22 голосов
/ 11 февраля 2011

Это работает, без всякого хулиганства с функциями:

func Reverse(s string) (result string) {
  for _,v := range s {
    result = string(v) + result
  }
  return 
}
15 голосов
/ 18 ноября 2009

Это работает со строками Unicode, учитывая 2 вещи:

  • диапазон работает со строкой, перечисляя символы Юникода
  • Строка может быть построена из кусочков типа int, где каждый элемент является символом 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);
}
11 голосов
/ 11 февраля 2011

Я заметил этот вопрос, когда Саймон опубликовал его решение , которое, поскольку строки неизменяемы, очень неэффективно. Другие предложенные решения также несовершенны; они не работают или они неэффективны.

Вот эффективное решение, которое работает, за исключением случаев, когда строка недопустима в формате 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 犬")))
}
11 голосов
/ 22 июня 2015

От Примеры проектов Go: 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)
}

Go Playground для переворота струны

После изменения строки "bròwn" правильный результат должен быть "nwòrb", а не "nẁorb".
Обратите внимание на могилу над буквой о.


Для сохранения символов Юникода, комбинирующих символы, такие как «as⃝df̅» с обратным результатом «f̅ds⃝a»,
пожалуйста, обратитесь к другому коду, указанному ниже:

http://rosettacode.org/wiki/Reverse_a_string#Go

5 голосов
/ 30 декабря 2015

Здесь слишком много ответов. Некоторые из них являются четкими дубликатами. Но даже с левой стороны трудно выбрать лучшее решение.

Итак, я просмотрел ответы, выбросил тот, который не работает для юникода, а также удалил дубликаты. Я проверил выживших, чтобы найти самый быстрый. Итак, вот результаты с атрибуцией (если вы заметили ответы, которые я пропустил, но стоит добавить, смело изменяйте тест):

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=.

5 голосов
/ 04 июня 2017

Я написал следующую 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")
}
3 голосов
/ 26 ноября 2013

Это самая быстрая реализация

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)
    }
}
3 голосов
/ 03 ноября 2014

Если вам нужно работать с кластерами графем, используйте модуль 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
}
...