Как заменить символ в строке golang?

Я знаю что в go немутабельные строки, каким образом я могу создать копию строки с измененным символом по определенному индексу? Все способы которые я виже, это переводить строку в массив рун и обратно, есть прсотой способ сделать это?


Ответы (3 шт):

Автор решения: Victor E.

Попробуйте использовать функции strings.Replace или strings.ReplaceAll

Пример использования:

package main

import (
    "fmt"
    "strings"
)

func main() {
    originalString := "My origin string"
    modifiedString := strings.Replace(originalString, "origin", "modified", -1)

    fmt.Println(originalString)
    fmt.Println(modifiedString)
}
→ Ссылка
Автор решения: Николай

Как насчёт использования среза?

package main

import "fmt"

func main() {
    str := "Hello, world!"
    index := 1
    replacement := "a"
    newStr := str[:index] + replacement + str[index+1:]
    fmt.Println(newStr)
}

Спасибо что подсказали, и да, это плохо работает с кириллицей разного количества байт.

Вот тестирование работы технологий для работы с кириллицей на тексте "Привет, мир!" с заменой одного индекса в 1000000 подходов:

Replace with loop took 8.3497ms
Replace with map took 14.7032ms
Replace with slice took 5.1805ms
Replace with builder took 7.7388ms
Replace with bytes took 5.9656ms
Replace with regexp took 33.3084ms
Replace with buffer took 7.6607ms
Replace with copy took 5.9557ms
Replace with ReplaceAll took 7.329ms

А вот результаты тестирования на тексте из 200 символов с заменой всех вхождения в тексте на 1000000 подходов:

Replace with loop took 3.1552988s
Replace with map took 3.18197s
Replace with slice took 14.301µs
Replace with builder took 15.29µs
Replace with bytes took 8.203µs
Replace with regexp took 26.181µs
Replace with buffer took 26.408µs
Replace with copy took 38.881µs
Replace with ReplaceAll took 30.534µs

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

→ Ссылка
Автор решения: Pak Uula

По необъяснимой причине вопрос всплыл спустя два года после публикации. Вдруг кому будет интересен ответ ))

Строки в Golang - это просто байтовые последовательности. Строковые литералы компилятор сохраняет в кодировке utf-8, поэтому для сохранения юникодности нужно работать не с байтами, а с юникодными символами в кодировке utf-8

Вот пример функции, которая в строке заменяет count юникодных символов, начиная с символа с номером pos:

// ReplaceUtf8 заменяет count рун в строке str начиная с руны на позиции pos
// предполагается, что repl корректная utf8 строка
// мы не проверяем, что после замены декодирование хвоста str не изменится
func ReplaceUtf8(str []byte, pos, count int, repl []byte) []byte {
    if len(repl) == 0 {
        return str
    }
    if count < 0 {
        return nil
    }
    idx := IndexNthRuneUtf8(str, pos)
    if idx < 0 {
        return nil
    }
    end := IndexNthRuneUtf8(str, pos+count)
    if end < 0 {
        return append(str[:idx], repl...)
    }
    result := make([]byte, idx+len(repl)+len(str[end:]))
    copy(result[idx+len(repl):], str[end:])
    copy(result[idx:], repl)
    copy(result[:], str[:idx])
    return result
}

Функция IndexNthRuneUtf8 ищет индекс в массиве, соответствующей n-ной руне

// находит положение руны с номером count в строке UTF-8
// возвращает -1 в случае неудачи
func IndexNthRuneUtf8(str []byte, count int) (idx int) {
    if count < 0 { 
        return -1 
    }
    var n int
    for i := 0; i < len(str); {
        if n == count {
            return i
        }
        _, size := utf8.DecodeRune(str[i:])
        i += size
        n++
    }
    return -1
}

Пример: https://go.dev/play/p/zINGd6QEFrv

func main() {
    str := "Привет, мир!"
    pos := 6 // after "т"
    len := 2 // ", "
    res := ReplaceUtf8([]byte(str), pos, len, []byte(" (о-го-го) "))
    // Привет (о-го-го) мир!
    fmt.Println(string(res))
}

результат

Привет (о-го-го) мир!
→ Ссылка