2014-07-22 9 views
11

Desidero sostituire una lettera in un indice specifico in una stringa: aaaaaaa ->aaabaaa. C'è un modo per farlo? Ho scritto la seguente funzione di supporto da utilizzare nel frattempo:Come sostituire una lettera in un indice specifico in una stringa in Go?

func main() { 
    input := "aaaaaaa" 
    output := replaceAtIndex(input, 'b', 3) 
} 

func replaceAtIndex(input string, replacement byte, index int) string { 
    return strings.Join([]string{input[:index], string(replacement), input[index+1:]}, "") 
} 

risposta

7

è possibile concatenare le stringhe con l'operatore +:

return input[:index] + string(replacement) + input[index+1:] 
+0

c'è qualche vantaggio in questo '' strings.Join'? – Ferguzz

14

stringhe sono immutabili nel Go, è necessario convertirlo in rune poi modificarlo e quindi riconvertirlo in una stringa. soluzione

@ di chendesheng è semi-corretta, tranne che si può utilizzare al posto di runebyte, in questo modo si lavorerà su unicode pure.

func replaceAtIndex(in string, r rune, i int) string { 
    out := []rune(in) 
    out[i] = r 
    return string(out) 
} 

playground

9

Entrambe le risposte (OneOfOne e Denys Séguret) siano corrette. Volevo solo mostrare la differenza di prestazioni tra di loro (che è davvero evidente quando la stringa è grande).

Si scopre che utilizzando str [: indice] + stringa (sostituzione) + str [indice + 1:] è molto più veloce.

Quindi il punto di riferimento:

package main 
import (
    "testing" 
) 

func replaceAtIndex1(str string, replacement rune, index int) string { 
    out := []rune(str) 
    out[index] = replacement 
    return string(out) 
} 

func replaceAtIndex2(str string, replacement rune, index int) string { 
    return str[:index] + string(replacement) + str[index+1:] 
} 

func generateString(n int) string{ 
    s := "" 
    for i := 0; i < n; i++{ 
     s += "a" 
    } 
    return s 
} 

func BenchmarkSmall1(b *testing.B) { 
    n := 10 
    str, index, replacement := generateString(n), n/2, 'B' 

    b.ResetTimer() 
    for i := 0; i < b.N; i++ { 
     replaceAtIndex1(str, replacement, index) 
    } 
} 

func BenchmarkSmall2(b *testing.B) { 
    n := 10 
    str, index, replacement := generateString(n), n/2, 'B' 

    b.ResetTimer() 
    for i := 0; i < b.N; i++ { 
     replaceAtIndex2(str, replacement, index) 
    } 
} 

func BenchmarkMedium1(b *testing.B) { 
    n := 100 
    str, index, replacement := generateString(n), n/2, 'B' 

    b.ResetTimer() 
    for i := 0; i < b.N; i++ { 
     replaceAtIndex1(str, replacement, index) 
    } 
} 

func BenchmarkMedium2(b *testing.B) { 
    n := 100 
    str, index, replacement := generateString(n), n/2, 'B' 

    b.ResetTimer() 
    for i := 0; i < b.N; i++ { 
     replaceAtIndex2(str, replacement, index) 
    } 
} 

func BenchmarkBig1(b *testing.B) { 
    n := 10000 
    str, index, replacement := generateString(n), n/2, 'B' 

    b.ResetTimer() 
    for i := 0; i < b.N; i++ { 
     replaceAtIndex1(str, replacement, index) 
    } 
} 

func BenchmarkBig2(b *testing.B) { 
    n := 10000 
    str, index, replacement := generateString(n), n/2, 'B' 

    b.ResetTimer() 
    for i := 0; i < b.N; i++ { 
     replaceAtIndex2(str, replacement, index) 
    } 
} 

func main(){} 

mostra i seguenti risultati (grazie Thomasz per avvistare un errore di copypasting):


BenchmarkSmall1-4 10000000   228 ns/op 
BenchmarkSmall2-4 10000000   126 ns/op 
BenchmarkMedium1-4 500000   2091 ns/op 
BenchmarkMedium2-4 10000000   190 ns/op 
BenchmarkBig1-4  10000  209232 ns/op 
BenchmarkBig2-4  500000   3629 ns/op 
+0

Questo non è un confronto equo. Tagliare str senza convertire in runa è inutile. – Amos

1

Solo per divertimento:

package main                                                 

import (                                                 
    "fmt"                                                 
    "reflect"                                                
    "syscall"                                                
    "unsafe"                                                 
)                                                   

// We should do this because by default strings in Go are read-only.                                                                                                     
func mprotect(ptr uintptr, w bool) {                                           
    // Need to avoid "EINVAL addr is not a valid pointer, 
    // or not a multiple of PAGESIZE."                             
    start := ptr & ^(uintptr(syscall.Getpagesize() - 1))                                      

    prot := syscall.PROT_READ                                            
    if w {                                                 
     prot |= syscall.PROT_WRITE                                           
    }                                                  

    _, _, err := syscall.Syscall(                                           
     syscall.SYS_MPROTECT,                                            
     start, uintptr(syscall.Getpagesize()),                                        
     uintptr(prot),                                              
    )                                                  
    if err != 0 {                                               
     panic(err.Error())                                             
    }                                                  
}                                                   

// This function is very, very very very unsafe.                                        
// Nowhere and never use it!                                             
func replaceAtIndex(s string, b byte, i int) {                                        
    h := *(*reflect.StringHeader)(unsafe.Pointer(&s))                                      

    mprotect(h.Data, true)                                             
    defer mprotect(h.Data, false)                                           

    *(*byte)(unsafe.Pointer(h.Data + uintptr(i))) = b                                      
}                                                   

func main() {                                                
    h := "Hello, playground"                                             
    replaceAtIndex(h, 'x', 0)                                            
    fmt.Println(h)                                               
} 

Non tentare mai di utilizzarlo da qualche parte nel codice. È più lento di qualsiasi soluzione standard o esempio sopra e molto più pericoloso. =)

(Non funziona nel parco giochi, perché syscall non è definito lì).

Problemi correlati