วิธีการย้อนกลับสตริงใน Go?


104

เราจะย้อนกลับสตริงง่ายๆใน Go ได้อย่างไร?


1
เท่าที่ผมเข้าใจการแก้ปัญหาที่ระบุด้านล่างจะไม่ได้ทำงานกับตัวละคร precomposed หรือการรวมเช่นให้แทนa+´ áฉันสงสัยว่าจะนำมาพิจารณาได้อย่างไรโดยไม่ทำให้เป็นปกติ
siritinga

หากคุณกำลังสับสนกับจำนวนมากของคำตอบที่คล้ายกันตรวจสอบมาตรฐานของฉัน
Salvador Dali

คำตอบ:


100

ใน Go1 rune เป็นประเภท builtin

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

4
คุณไม่สามารถใช้ len () ใน Go เพื่อหาความยาวของสตริง / Array / Slice ฯลฯ ... นี่คือเหตุผล - len () ใน Go หมายถึงขนาดของอินพุตเป็นไบต์ ไม่สอดคล้องกับความยาว - รูนของ utf8 ไม่ได้มีขนาดเท่ากันทั้งหมด อาจเป็นได้ทั้ง 1, 2, 4 หรือ 8 - คุณควรใช้วิธีการ RuneCountInString ของแพ็คเกจ Unicode / ut8 เพื่อรับความยาวของรูน
Anvesh Checka

17
@AnveshChecka นั่นไม่ถูกต้อง ดูgolang.org/pkg/builtin/#len - len () บนชิ้นจะส่งคืนจำนวนองค์ประกอบอย่างแน่นอนไม่ใช่ขนาดเป็นไบต์ รูนฝานเป็นวิธีที่ถูกต้องในการทำ
chowey

4
@ рытфолдสิ่งนี้ใช้ไม่ได้กับการรวมอักขระ ดูplay.golang.org/p/sBgZAV7gCbอักขระที่รวมกันจะไม่ถูกสลับกับฐาน
chowey

53

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

20
ฉันชอบวิธีที่พวกเขาบังคับให้คุณคิดเกี่ยวกับการเข้ารหัส
György Andrasek

11
นอกประเด็น: ทำไมถึงเป็น [โกแลง - นัท] และไม่ใช่ [โกแลต]?
Jimmy

2
ว้าว wtf ขึ้นอยู่กับการกำหนดสองครั้งเมื่อย้อนกลับ? น่าสนใจ. ตอนนี้ให้คิดถึงสตริงที่มีจำนวนรูนไม่เท่ากัน คนกลางได้รับการดูแลเป็นพิเศษพร้อมผลลัพธ์สุดท้ายที่ถูกต้อง :) การเพิ่มประสิทธิภาพเล็ก ๆ น้อย ๆ ที่น่าสนใจที่ฉันคิดไม่ถึงในทันที
Kissaki

4
ฉันไม่เข้าใจว่าทำไมถึงเปลี่ยนเป็นรูนทำไมไม่rune:=[]rune(input)?
siritinga

1
คุณไม่ต้องการสิ่งแรกสำหรับลูปช่วง เอาท์พุท: = [] รูน (อินพุต); n: = len (เอาต์พุต) และคุณไม่จำเป็นต้องใช้ rune = rune [0: n]
dvallejo

30

ใช้งานได้โดยไม่ต้องยุ่งเกี่ยวกับฟังก์ชัน:

func Reverse(s string) (result string) {
  for _,v := range s {
    result = string(v) + result
  }
  return 
}

7
แม้ว่าจะใช้งานได้เนื่องจากสตริงไม่เปลี่ยนรูปจึงไม่มีประสิทธิภาพมาก ฉันได้โพสต์วิธีแก้ปัญหาที่มีประสิทธิภาพมากขึ้น
peterSO

5
นี้เป็นมากง่ายเกินไปที่จะเข้าใจ ทำให้มันยากขึ้น :-) (และ "บวกหนึ่ง" สำหรับการก้าวต่อไป)
Roboprog

2
นี่คือคำตอบที่ดีที่สุดเว้นแต่การย้อนกลับสตริงจะเป็นคอขวดของคุณ
Banjocat

1
@dolmen - ทำไมสิ่งนี้ถึงไม่จัดการกับการรวมตัวละคร? ช่วงบนสตริงส่งคืนรูนซึ่งเป็นจุดรหัส
สแตนอา

2
@ สแตนอาร์. รูนไม่ใช่สัญลักษณ์ ร่ายมนตร์สามารถสร้างได้จากจุดรหัส / รูนหลายตัว ดูreedbeta.com/blog/programmers-intro-to-unicode/#combining-marksจุดรหัสย้อนกลับจะแนบเครื่องหมายรวมเข้ากับจุดรหัสฐานอื่น
dolmen

14

สิ่งนี้ใช้ได้กับสตริง Unicode โดยพิจารณา 2 สิ่ง:

  • rangeทำงานกับสตริงโดยการระบุอักขระ Unicode
  • สตริงสามารถสร้างจากชิ้นส่วน 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);
}

ฉันจะกำหนดและจากนั้นพับสำหรับเป็นบรรทัดเดียวi:=len(o)-1 for _, c:=range s { o[i--]=c; }ผู้ชายที่ฉันเกลียดการไม่มีวงเล็บ - อนุญาตหรือไม่:for(_, c:=range s) { o[i--]=c; }
Lawrence Dol

คุณช่วยอธิบายได้ไหมว่า _ ทำอะไร?
Lawrence Dol

7
@Software_Monkey: o [i--] = c ไม่ได้รับอนุญาตใน Go - และ ++ เป็นคำสั่งไม่ใช่นิพจน์ _ หมายถึงการทิ้ง (ละเว้น) ตัวแปรนั้น
Randy Sugianto 'Yuku'

1
ด้วย go 1.1+ จะส่งกลับข้อผิดพลาดในสาย string ([] int) ถ้าใช้ประเภท rune แทนสำหรับ o จะใช้ได้ทั้งหมด
Otuk

1
@yuku: ยังคงล้มเหลวใน s: = "Les Mise \ u0301rables"
Stefan Steiger

13

จากโครงการตัวอย่าง 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)
}

ไปที่ Playground เพื่อย้อนกลับสตริง

หลังจากย้อนกลับสตริง "bròwn" ผลลัพธ์ที่ถูกต้องควรเป็น "nwòrb" ไม่ใช่ "nẁorb"
สังเกตหลุมฝังศพเหนือตัวอักษร o


สำหรับการเก็บรักษา Unicode ที่รวมอักขระเช่น "as⃝df̅" ด้วยผลลัพธ์ย้อนกลับ "f̅ds⃝a"
โปรดดูรหัสอื่นที่แสดงด้านล่าง:

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


2
ขอขอบคุณที่ชี้แจงความแตกต่างจากstackoverflow.com/a/10030772/3093387ดูเหมือนว่าโซลูชันทั้งสองนี้จะแตกต่างกันในวิธีจัดการสตริงเช่น "bròwn"
josliber

ขอขอบคุณที่กล่าวถึงโซลูชัน Rosettacode ที่จัดการอักขระแบบผสมผสาน
dolmen

11

ฉันสังเกตเห็นคำถามนี้เมื่อ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 犬")))
}

1
return string (รูน) ใช้งานได้เช่นกัน

3
@ ทอมมี่: return string(runes)ไม่ใช้ได้ทุกกรณี
peterSO

คุณช่วยอธิบายเพิ่มเติมเกี่ยวกับสาเหตุนั้นได้ไหม ฉันทำโปรแกรมสั้น ๆ และใช้งานได้ที่นั่น แต่บางทีกรณีที่คุณพูดถึงไม่ได้เกิดขึ้นที่นั่น? play.golang.org/p/yk1sAwFjol

1
@ ทอมมี่: โปรแกรมสั้น ๆ ของคุณเพียงแสดงให้เห็นว่าอักขระ NUL เป็น NOP เมื่อส่งไปยังเครื่องพิมพ์หรือเทอร์มินัล ฟังก์ชัน Reverse2 ของคุณล้มเหลวสำหรับสตริงที่เข้ารหัสที่ไม่ใช่ ASCII UTF-8 ฉันได้แก้ไขโปรแกรมสั้น ๆ ของคุณเพื่อให้เป็นการทดสอบที่ถูกต้อง: play.golang.org/p/Ic5G5QEO93
peterSO

"วิธีแก้ปัญหา" ที่ไม่ถูกต้องอีกวิธีหนึ่งที่จัดการการรวมอักขระไม่ถูกต้อง
dolmen

11

มีคำตอบมากเกินไปที่นี่ บางส่วนเป็นรายการที่ซ้ำกันชัดเจน แต่ถึงแม้จะเลือกจากทางซ้ายก็ยากที่จะเลือกวิธีแก้ปัญหาที่ดีที่สุด

ดังนั้นฉันจึงอ่านคำตอบทิ้งคำตอบที่ใช้ไม่ได้กับ 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=.


ไม่มี "โซลูชัน" ใดที่จัดการกับเครื่องหมายรวมอย่างถูกต้อง
dolmen

7

ฉันเขียน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")
}

เพื่อให้ห่างไกลนี้เป็นคำตอบเดียวที่ทำงานร่วมกับสตริงที่มีตัวอักษรคลัสเตอร์อักษร (ตัวอักษรรวมกัน) เช่นที่ฝืนไป🙏2️⃣👌👁 👁👌2️⃣🙏ส่วนใหญ่คำตอบอื่น ๆ 👁👌⃣️2🙏กลับมา paiza.io/projects/K8_kOmxn-cGRR3ksh1vFtw
KEINOS

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

การใช้สตริง Builder เร็วกว่าการใช้การต่อสตริงประมาณ 3 เท่า


1
ฉันสงสัยว่าทำไมคำถามนี้ถึงไม่มีการโหวตเพิ่มทั้งๆที่เป็นคำตอบที่ถูกต้องที่สุด
Nilesh

4

ที่นี่ค่อนข้างแตกต่างกันฉันจะพูดถึงแนวทางการทำงานมากกว่าซึ่งไม่ได้ระบุไว้ในคำตอบอื่น ๆ :

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

ฉันค่อนข้างแน่ใจว่ามันไม่ใช่วิธีแก้ปัญหาที่เร็วที่สุด แต่มันแสดงให้เห็นว่าตัวแปร return retถูกปิดไว้เพื่อการประมวลผลต่อไปอย่างไรโดยแต่ละฟังก์ชัน defer
Vladimir Bauer

ช้าและจัดการการรวมอักขระไม่ถูกต้อง
dolmen

1
ฉันไม่แน่ใจว่าเร็วแค่ไหน แต่มันสวยงาม
donatJ

ประสิทธิภาพของเครื่องนี้อาจได้รับการปรับปรุงใน Go 1.14 อย่างน้อยบันทึกประจำรุ่นอ้างว่าไม่มีค่าใช้จ่ายในการเลื่อนเวลาออกไป
Vladimir Bauer

3

สร้างตามข้อเสนอแนะดั้งเดิมของ 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 );
}

+1. ที่ได้ผล แต่ฉันต้องบอกว่ามันค่อนข้างแปลก (สำหรับตอนนี้) ที่การแยกและการเข้าร่วมเป็นสิ่งที่จำเป็นสำหรับงานง่ายๆเช่นนี้ ...
Stephan202

@martin: ขออภัยสำหรับการแก้ไขนั้น ฉันตั้งใจวางคำตอบของฉันมีการปรับปรุงในคำถามของคุณ ... ฉันละอายใจมาก
Stephan202

@Stephan - ไม่มีปัญหา ฉันเพิ่มโซลูชันทางเลือกโดยยึดตามฟังก์ชันไบต์แพ็กเกจสตริง
martin Clayton

@Nosradena: ฉันย้อนกลับไปภายในนาทีเดียวกัน (ฉันรู้สึกประหลาดใจที่เห็นว่ามาร์ตินอัปเดตคำตอบของเขาด้วยข้อความเดียวกับที่ฉันเพิ่งเขียน ... แล้วมันก็เริ่มขึ้นกับฉัน;)
Stephan202

@martin: รุ่นที่สองดูดีกว่าถ้าคุณถามฉัน :)
Stephan202

3

นี่คือการใช้งานที่เร็วที่สุด

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

คุณได้เปรียบเทียบโซลูชันก่อนที่จะอ้างว่าเป็นการใช้งานที่เร็วที่สุดหรือไม่?
Denys Séguret

ใช่ฉันทำนั่นคือสาเหตุที่มีรหัส BenchmarkReverse :) อย่างไรก็ตามฉันไม่มีผลลัพธ์อีกต่อไป
rmuller

วิธีแก้ปัญหาอย่างรวดเร็ว แต่ยังผิดพลาดเนื่องจากไม่สามารถจัดการกับการรวมอักขระได้อย่างเหมาะสม
dolmen

เป็นความจริงอย่างที่ @dolmen กล่าวว่าสิ่งนี้ไม่สามารถจัดการกับการรวมอักขระได้หรือไม่? มีวิธีแก้ไขที่นี่หรือไม่?
geraldss

2

รหัสนี้เก็บรักษาลำดับของการรวมอักขระที่เหมือนเดิมและควรใช้กับอินพุต 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


ไม่มีเช่น "ไม่ถูกต้อง UTF-8 การป้อนข้อมูล" ในค่าสตริงคือเมื่อมีการแปลงจาก[]byteที่จะstringไปแทนที่ "ไม่ถูกต้อง UTF-8 การป้อนข้อมูล" \uFFFDโดยจุดโค้ดที่ถูกต้อง
dolmen

ฉันไม่เข้าใจความคิดเห็นด้านบน คุณกำลังบอกว่าการทำงานของรหัสนี้ผิดเมื่อแสดงด้วยสตริงที่มี UTF-8 ที่ไม่ถูกต้องใช่หรือไม่
rog

ไม่ฉันกำลังบอกว่า UTF-8 ที่ไม่ถูกต้องใน Go stringไม่มีอยู่ แต่สามารถมีอยู่ในไฟล์[]byte.
dolmen

สตริง Go สามารถมี utf-8 ที่ไม่ถูกต้องเท่ากับ [] ไบต์ ตัวอย่างเช่นplay.golang.org/p/PG0I4FJfEN
rog

2

หากคุณต้องการจัดการกับคลัสเตอร์ 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
}

ฉันอยากจะให้คุณ 1'000 upvotes การใช้งานอื่น ๆ ทั้งหมดในหน้านี้กลับ STRING ไม่ถูกต้อง (STRING ไม่ใช่ลำดับของอักขระ)
Stefan Steiger

วิธีนี้ใช้ไม่ได้ หากคุณย้อนกลับสตริงสองครั้งคุณจะไม่ได้รับสตริงเดิม การรวม Diaeresis ชั้นนำ (\ u0308) ที่ใช้ในตัวอย่างนี้จะรวมกับอักขระก่อนหน้าซึ่งสร้างเครื่องหมาย umlaut 'a' สองครั้งเมื่อกลับด้าน หากstrเอาต์พุตที่ยกมามันจะปรับเปลี่ยนคำพูดชั้นนำ!
Joshua Kolden

2

คุณยังสามารถนำเข้าการใช้งานที่มีอยู่:

import "4d63.com/strrev"

จากนั้น:

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

หรือเพื่อย้อนกลับสตริงรวมถึงอักขระที่รวมยูนิโคด:

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

การใช้งานเหล่านี้สนับสนุนการเรียงลำดับที่ถูกต้องของหลายไบต์ยูนิโคดและการรวมอักขระเมื่อย้อนกลับ

หมายเหตุ: ฟังก์ชันย้อนกลับสตริงในตัวในภาษาการเขียนโปรแกรมหลายภาษาไม่เก็บการรวมไว้และการระบุอักขระที่รวมต้องใช้เวลาดำเนินการมากขึ้นอย่างมาก


1

แน่นอนว่าไม่ใช่โซลูชันที่มีประสิทธิภาพหน่วยความจำมากที่สุด แต่สำหรับโซลูชันที่ปลอดภัยแบบ UTF-8 แบบ "ธรรมดา" สิ่งต่อไปนี้จะทำให้งานสำเร็จและไม่ทำลายรูน

ในความคิดของฉันอ่านได้และเข้าใจได้มากที่สุดในหน้านี้

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

    return
}

1

สองวิธีต่อไปนี้ทำงานได้เร็วกว่าวิธีแก้ปัญหาที่เร็วที่สุดที่รักษาการรวมอักขระไว้แม้ว่าจะไม่ได้หมายความว่าฉันขาดบางอย่างในการตั้งค่าเกณฑ์มาตรฐาน

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

นี่คือสิ่งที่คุณขาดหายไปในเกณฑ์มาตรฐานของคุณ: โซลูชันของคุณเร็วกว่าเพราะไม่ได้รักษาการรวมอักขระไว้ มันไม่ยุติธรรมที่จะเปรียบเทียบพวกเขา
dolmen

1

หมายเหตุ:คำตอบนี้มาจากปี 2009 ดังนั้นในตอนนี้อาจมีวิธีแก้ปัญหาที่ดีกว่า


มีลักษณะเป็น 'วงเวียน' เล็กน้อยและอาจไม่มีประสิทธิภาพมากนัก แต่แสดงให้เห็นว่าอินเทอร์เฟซ Reader สามารถใช้อ่านจากสตริงได้อย่างไร IntVectors ดูเหมือนว่าเหมาะมากสำหรับเป็นบัฟเฟอร์เมื่อทำงานกับสตริง utf8

มันจะยิ่งสั้นลงเมื่อละส่วน 'ขนาด' ออกและการแทรกลงในเวกเตอร์โดยการแทรก แต่ฉันเดาว่ามันจะมีประสิทธิภาพน้อยกว่าเนื่องจากเวกเตอร์ทั้งหมดจะต้องถูกผลักกลับทีละครั้งทุกครั้งที่มีการเพิ่มรูนใหม่ .

โซลูชันนี้ใช้ได้กับอักขระ 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());
}

ทำไมคุณเพิ่มอัฒภาคต่อท้ายทั้งหมด
Morteza R

@ olivier-mason ถึงเวลาเรียนรู้เกี่ยวกับ gofmt เมื่อแชร์ Go code
dolmen

1
คำตอบนั้นมาจากแปดปีที่แล้ว
Oliver Mason

@OliverMason ไม่เคยสายเกินไปที่จะแก้ไข (หรือลบ) โซลูชันที่ไม่สมบูรณ์แบบ
dolmen

0

เวอร์ชันที่ฉันคิดว่าใช้ได้กับ 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);
}

0

รูนเป็นประเภทดังนั้นจงใช้มัน ยิ่งไปกว่านั้น 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))
}

ใช้เครื่องหมายอัฒภาคเมื่อมีการโพสต์คำถามนั้น
OneOfOne

"วิธีแก้ปัญหา" ที่ไม่ถูกต้องอีกวิธีหนึ่งที่จัดการการรวมอักขระไม่ถูกต้อง
dolmen

0

ลองใช้รหัสด้านล่าง:

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


0

สำหรับสตริงง่ายๆสามารถใช้โครงสร้างดังกล่าวได้:

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

0

จังหวะง่ายๆด้วย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

0
func Reverse(s string) string {
    r := []rune(s)
    var output strings.Builder
    for i := len(r) - 1; i >= 0; i-- {
        output.WriteString(string(r[i]))
    }

    return output.String()
}

ขอบคุณสำหรับคำตอบ แต่ด้วยคำตอบอื่น ๆ 32 คำและบางคำที่เหมือนกันจริง ๆ สิ่งนี้เพิ่มอะไรใหม่ให้กับวาทกรรมหรือไม่? ถ้าเป็นเช่นนั้นโปรดอธิบายเหตุผลที่มีประโยชน์ คำตอบที่ใช้รหัสเท่านั้นถือว่ามีคุณภาพต่ำ
ggorlen

-1

นี่เป็นวิธีแก้ปัญหาอื่น:

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ชิ้นส่วนเข้าที่


-1

อีกวิธีหนึ่ง (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)
}

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

สิ่งนี้ใช้ได้หากอินพุตอยู่ใน NFC แต่วิธีแก้ปัญหาอื่น ๆ ที่ไม่ถูกต้องส่วนใหญ่ที่นี่ใช้ไม่ได้กับการรวมอักขระ
dolmen

-1
    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
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.