Удалить недопустимые символы UTF-8 из строки

Я получаю это на json.Marshal из списка строк:

json: invalid UTF-8 in string: "...ole\xc5\"

Причина очевидна, но как я могу удалить или заменить такие строки в Go? Я читал docst в пакетах unicode и unicode/utf8, и нет очевидного/быстрого способа сделать это.

В Python, например, у вас есть методы для него, где недопустимые символы могут быть удалены, заменены указанным символом или строгим параметром, который вызывает исключение на недопустимых символах. Как я могу сделать эквивалентную вещь в Go?

UPDATE: Я имел в виду причину получения исключения (panic?) - незаконный char в том, что json.Marshal ожидает быть допустимой строкой UTF-8.

(как незаконная последовательность байтов попала в эту строку, не имеет значения, обычный способ - ошибки, повреждение файлов, другие программы, которые не соответствуют юникоду и т.д.)

Ответ 1

Например,

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    s := "a\xc5z"
    fmt.Printf("%q\n", s)
    if !utf8.ValidString(s) {
        v := make([]rune, 0, len(s))
        for i, r := range s {
            if r == utf8.RuneError {
                _, size := utf8.DecodeRuneInString(s[i:])
                if size == 1 {
                    continue
                }
            }
            v = append(v, r)
        }
        s = string(v)
    }
    fmt.Printf("%q\n", s)
}

Вывод:

"a\xc5z"
"az"

Стандарт Unicode

FAQ - UTF-8, UTF-16, UTF-32 и спецификация

Q: Существуют ли последовательности байтов, которые не генерируются UTF? Как Должен ли я их интерпретировать?

A: Ни один из UTF не может генерировать каждую произвольную последовательность байтов. Для Например, в UTF-8 должен соблюдаться каждый байт формы 110xxxxx2 с байтом формы 10xxxxxx2. Последовательность, такая как < 110xxxxx2 0xxxxxxx2 > является незаконным и никогда не должен генерироваться. Когда эта незаконная последовательность байтов при преобразовании или интерпретации, UTF-8 соответствующий процесс должен обрабатывать первый байт 110xxxxx2 как незаконный ошибка завершения: например, сигнализация об ошибке, фильтрация байта или представление байта с маркером, таким как FFFD (ХАРАКТЕР ЗАМЕНЫ). В последних двух случаях он будет продолжаться обработка второго байта 0xxxxxxx2.

Сопоставительный процесс не должен интерпретировать незаконный или плохо сформированный байт последовательности как символы, однако он может принимать действия по восстановлению ошибок. Никакой соответствующий процесс не может использовать нерегулярные последовательности байтов для кодирования внеполосная информация.

Ответ 2

Начиная с Go 1.13, вы также сможете делать что-то вроде этого:

strings.ToValidUTF8("a\xc5z", "")

В Go 1.11 это также очень легко сделать с помощью функции карты и utf8.RuneError, например:

fixUtf := func(r rune) rune {
    if r == utf8.RuneError {
        return -1
    }
    return r
}

fmt.Println(strings.Map(fixUtf, "a\xc5z"))
fmt.Println(strings.Map(fixUtf, "posic�o"))

Выход:

az
posico

Детская площадка: Здесь.