如何替换Go中字符串中特定索引处的字母?

Fer*_*uzz 17 string go

我想替换字符串中特定索引处的字母:aaaaaaa- > aaabaaa.有没有内置的方法来做到这一点?我编写了以下辅助函数来同时使用:

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:]}, "")
}
Run Code Online (Sandbox Code Playgroud)

One*_*One 22

字符串在Go中是不可变的,你必须将其转换为符文然后修改它然后将其转换回字符串.

@ chendesheng的解决方案是半正确的,除了你可以使用rune而不是byte,这样它也可以用于unicode.

func replaceAtIndex(in string, r rune, i int) string {
    out := []rune(in)
    out[i] = r
    return string(out)
}
Run Code Online (Sandbox Code Playgroud)

playground


Sal*_*ali 15

这两个答案(OneOfOneDenysSéguret)都是正确的.我只想展示它们之间的性能差异(当字符串很大时这是非常明显的).

事实证明,使用str [:index] + string(替换)+ str [index + 1:]会更快.

所以基准:

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(){}
Run Code Online (Sandbox Code Playgroud)

显示以下结果(感谢Thomasz发现一个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
Run Code Online (Sandbox Code Playgroud)

  • 这不是一个公平的比较。切片 str 而不转换为 rune 是没有用的。 (2认同)
  • str[:index] 就像操作字节一样,它和符文完全不同,你可能会以这种方式破坏符文。例如,你可以使用“良好,你好世界”作为字符串。 (2认同)

Den*_*ret 8

您可以使用+运算符连接字符串:

return input[:index] + string(replacement) + input[index+1:]
Run Code Online (Sandbox Code Playgroud)


gob*_*was 6

只是为了好玩:

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)                                                                                                                                                                                          
}
Run Code Online (Sandbox Code Playgroud)

永远不要尝试在代码中的某处使用它。它比上面的任何标准解决方案或示例都慢,而且更不安全。=)

(它在操场上不起作用,因为syscall那里没有定义)。