string 配列 Goで文字列を逆にする方法は?




golang 配列 逆順 (20)

Goの簡単な文字列をどうやって元に戻すことができますか?


確かにメモリ効率のよいソリューションではありませんが、「シンプルな」UTF-8の安全なソリューションでは、次のようにすれば、仕事を終わらせることができます。

私の意見では、このページで最も読みやすく分かりやすいです。

func reverseStr(str string) (out string) {
    for _, s := range str {
        out = string(s) + out
    }

    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綱スライスを逆転させるので、よりエレガントです。


これは最も速い実装です

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)
    }
}

次の2つの方法は、 組み合わせ文字を保存する最速のソリューションよりも速く実行されますが、ベンチマークセットアップで何かが失われているとは限りません。

//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

this触発された第2の方法

//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

このコードは、文字の組み合わせのシーケンスをそのまま保持し、無効な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も参照してhttps://code.google.com/p/go/issues/detail?id=9055


ここではあまりにも多くの答えがあります。 それらのいくつかは明確な重複です。 しかし、左からでも、最良のソリューションを選択するのは難しいです。

だから私は答えを見て、ユニコードでは動作しないものを投げ捨て、重複を取り除いた。 生存者をベンチマークして最速のものを見つけました。 だから帰属の結果があります(私が逃した答えに気づいたら、追加する価値はありますが、ベンチマークを自由に変更できます):

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=.を実行してgo test -bench=.


これは2つのことを考慮してユニコード文字列で動作します:

  • 範囲はユニコード文字を列挙して文字列に作用します
  • 文字列は、各要素がユニコード文字であるintスライスから構築できます。

だからここに行く:

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);
}

Goプロジェクトの例:Andrew Gerrandによる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)
}

ストリングを逆にするためにPlaygroundを移動する

文字列 "bròwn"を反転した後、正しい結果は "nẁorb"ではなく "nwòrb"でなければなりません。
手紙oの上の墓に注意してください。

逆アセンブル結果 "f̅ds]a"のような "asceptiondf such"のような文字を組み合わせるUnicodeを保持するために、
下記の別のコードを参照してください:

http://rosettacode.org/wiki/Reverse_a_string#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)
}

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
}

Golang-NutsメーリングリストのRuss Cox氏は、

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)
}

私は、UTF8エンコーディングと結合文字を考慮して次のReverse関数を書いた:

// 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)
}

私は可能な限り効率的かつ可読性にするために最善を尽くしました。 アイデアはシンプルで、結合された文字を探しているルーンをたどり、結合された文字のルーンをその場で逆転させます。 一度すべてをカバーしたら、文字列全体のルーンをその場で元に戻します。

この文字列を逆にしたいとします。 òは、2つのルーンで表され、1つはoためのものであり、もう1つはこの「墓」を表すユニコードのものです。

簡単にするために、このような文字列を表現してみましょう。 最初に行うことは、結合された文字を探し、それらを逆にすることです。 だから我々は文字列br'ownを持っています。 最後に、文字列全体を逆にしてnwo'rbnwo'rbます。 これはnwò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")
}

既存の実装をインポートすることもできます:

import "4d63.com/strrev"

次に:

strrev.Reverse("abåd") // returns "dåba"

または、ユニコード結合文字を含む文字列を反転する:

strrev.ReverseCombining("abc\u0301\u031dd") // returns "d\u0301\u031dcba"

これらの実装では、逆の場合にユニコードマルチバイトとコーミング文字の正しい順序付けがサポートされています。

注:多くのプログラミング言語で組み込みのストリング逆関数は結合を保持せず、結合文字を識別するためにはかなり長い実行時間が必要です。


ここではかなり異なっている、私はより多くの機能的なアプローチを言うでしょう、他の答えの中に記載されていない:

func reverse(s string) (ret string) {
    for _, v := range s {
        defer func(r rune) { ret += string(r) }(v)
    }
    return
}

Credits


ユニコードで動くと思うバージョン。 これは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);
}

単純な文字列の場合、そのような構造を使用することができます:

func Reverse(str string) string {
    if str != "" {
        return Reverse(str[1:]) + str[:1]
    }
    return ""   
}

Stephan202のオリジナルの提案に基づいて構築され、ユニコード文字列で動作するようです。

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 );
}

ルーンはタイプなので使用してください。 さらに、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))
}


私はが文字列が不変であるために非常に効率が悪い解決策を投稿しときにこの問題に気付きました。 他の提案された解決策にも欠陥がある。 彼らは働かないか、あるいは非効率的です。

文字列が有効な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 犬")))
}




reverse