Вы можете удалить \x00
рун из строки так же, как вы можете удалить любые другие руны:
valueStr = strings.Replace(valueStr, "\x00", "", -1)
Пример:
s := "a\x00b"
fmt.Printf("%q\n", s)
s = strings.Replace(s, "\x00", "", -1)
fmt.Printf("%q\n", s)
Вывод (попробуйте на Go Playground ):
"a\x00b"
"ab"
Использование strings.Replacer
Также обратите внимание, что вы можете заменить несколько замен одной операцией, используя strings.Replacer
, и он также будет более эффективным, поскольку он будет повторять только один раз вход (и для результата будет выделен только один string
независимо от того, сколько подстрок вы хотите заменить).
Например:
s := " \t\n\rabc\x00"
fmt.Printf("%q\n", s)
r := strings.NewReplacer(" ", "", "\t", "", "\n", "", "\r", "", "\x00", "")
s = r.Replace(s)
fmt.Printf("%q\n", s)
Вывод (попробуйте на Go Playground ):
" \t\n\rabc\x00"
"abc"
Также обратите внимание, что достаточно создать string.Replacer
один раз,и вы можете сохранить его в (глобальной) переменной и использовать повторно, даже безопасно использовать его одновременно с несколькими программами.
Использование strings.Map()
Также обратите внимание, что если вы хотите толькочтобы заменить (удалить) одиночные rune
s, а не многорядные (или многобайтовые) подстроки, вы также можете использовать strings.Map()
, который может быть даже более эффективным, чем strings.Replacer
.
Сначала определите функцию, которая сообщает, какие rune
s заменить (или удалить, если вы возвращаете отрицательное значение):
func remove(r rune) rune {
switch r {
case ' ', '\t', '\n', '\r', 0:
return -1
}
return r
}
И затем с его помощью:
s := " \t\n\rabc\x00"
fmt.Printf("%q\n", s)
s = strings.Map(remove, s)
fmt.Printf("%q\n", s)
Вывод (попробуйте на Go Playground ):
" \t\n\rabc\x00"
"abc"
Тесты
Мы могли быдумаю, что strings.Map()
будет лучше, поскольку он должен иметь дело только с rune
s, которые являются просто int32
числами, в то время как strings.Replacer
должен иметь дело с string
значениями, которые являются заголовками (длина + указатель данных) плюс рядбайтов.
Но мы должны знать, что значения string
хранятся в памяти как байтовые последовательности UTF-8, что означает, что strings.Map()
необходимо декодировать rune
из байтовой последовательности UTF-8 (и закодировать руны обратно в UTF-8 в конце), в то время как strings.Replacer
этого не делает: он может просто искать совпадения последовательности байтов без декодирования rune
s.И strings.Replacer
высоко оптимизирован, чтобы использовать в своих интересах такие «уловки».
Итак, давайте создадим тест для сравнения:
Мы будем использовать их для тестов:
var r = strings.NewReplacer(" ", "", "\t", "", "\n", "", "\r", "", "\x00", "")
func remove(r rune) rune {
switch r {
case ' ', '\t', '\n', '\r', 0:
return -1
}
return r
}
И мы запускаем тесты для разных строк ввода:
func BenchmarkReplaces(b *testing.B) {
cases := []struct {
title string
input string
}{
{
title: "None",
input: "abc",
},
{
title: "Normal",
input: " \t\n\rabc\x00",
},
{
title: "Long",
input: "adsfWR \t\rab\nc\x00 \t\n\rabc\x00asdfWER\n\r",
},
}
for _, c := range cases {
b.Run("Replacer-"+c.title, func(b *testing.B) {
for i := 0; i < b.N; i++ {
r.Replace(c.input)
}
})
b.Run("Map-"+c.title, func(b *testing.B) {
for i := 0; i < b.N; i++ {
strings.Map(remove, c.input)
}
})
}
}
А теперь давайте посмотрим результаты теста:
BenchmarkReplaces/Replacer-None-4 100000000 12.3 ns/op 0 B/op 0 allocs/op
BenchmarkReplaces/Map-None-4 100000000 16.1 ns/op 0 B/op 0 allocs/op
BenchmarkReplaces/Replacer-Normal-4 20000000 92.7 ns/op 6 B/op 2 allocs/op
BenchmarkReplaces/Map-Normal-4 20000000 92.4 ns/op 16 B/op 2 allocs/op
BenchmarkReplaces/Replacer-Long-4 5000000 234 ns/op 64 B/op 2 allocs/op
BenchmarkReplaces/Map-Long-4 5000000 235 ns/op 80 B/op 2 allocs/op
Несмотря на ожидания, string.Replacer
работает довольно хорошо,так же хорошо, как strings.Map()
, потому что нет необходимости декодировать и кодировать руны.