【问题标题】:Remove invalid UTF-8 characters from a string从字符串中删除无效的 UTF-8 字符
【发布时间】:2013-12-22 12:06:00
【问题描述】:

我在 json.Marshal 的字符串列表中得到了这个:

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

原因很明显,但是如何在 Go 中删除/替换这些字符串?我一直在阅读有关 unicodeunicode/utf8 软件包的文档,但似乎没有明显/快速的方法。

例如,在 Python 中,您有一些方法可以删除无效字符,将其替换为指定字符或严格设置,这会在无效字符上引发异常。我怎样才能在 Go 中做同样的事情?

更新:我的意思是得到异常的原因(恐慌?) - json.Marshal 期望是有效的 UTF-8 字符串中的非法字符。

(非法字节序列如何进入该字符串并不重要,通常的方式 - 错误、文件损坏、其他不符合 unicode 的程序等)

【问题讨论】:

  • 原因何在?我猜你那里有一个 latin1(或 ISO8859 的其他变体)字符串,在这种情况下,你不希望函数吞下这些字符,而是在继续之前将它们转换为 UTF-8 ...
  • 在 Go 1.2 中,json 解析器将接受格式错误的 UTF-8。它将用替换字形替换格式错误的字节。

标签: json unicode go


【解决方案1】:

在 Go 1.13+ 中,您可以这样做:

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

在 Go 1.11+ 中,使用 Map functionutf8.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

游乐场: Here.

【讨论】:

【解决方案2】:

例如,

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 Standard

FAQ - UTF-8, UTF-16, UTF-32 & BOM

问:是否有任何不是由 UTF 生成的字节序列?如何 我应该解释它们吗?

答:没有一个 UTF 可以生成每个任意字节序列。为了 例如,在 UTF-8 中,必须遵循 110xxxxx2 形式的每个字节 带有 10xxxxxx2 形式的字节。 是非法的,绝对不能生成。面对时 在转换或解释这个非法字节序列时,一个 UTF-8 一致性进程必须将第一个字节 110xxxxx2 视为非法 终止错误:例如,要么发出错误信号,要么过滤 字节输出,或用 FFFD 等标记表示字节 (替换字符)。后两种情况会继续 在第二个字节 0xxxxxxx2 处处理。

一致的进程不能解释非法或格式错误的字节 序列作为字符,但是,它可能会采取错误恢复操作。 没有一致的进程可以使用不规则的字节序列进行编码 带外信息。

【讨论】:

  • 虽然很可能完全不相关,但如果字符串还包含损坏的 UTF8 序列,您的示例可能会删除完全正确的编码 Unicode 替换字符 ("\xef\xbf\xbd")。
  • @ANisus:假设人们已经阅读了 Unicode 标准。
  • 我的评论只是琐事。我的函数还将删除替换字符以及非法序列(毕竟是我的 +1 ;))。刚才说了json.Marshal会接受的“\xef\xbf\xbd”的合法字节序列也会被剥离掉。不确定 Unicode 标准会如何不同意这一点。
  • @ANisus:如果你愿意,你可以保留任何替换字符。请参阅我修改后的答案。
  • @Roylee:同样的东西,不同的名字:unicode.ReplacementChar = '\uFFFD'utf8.RuneError = '\uFFFD'
【解决方案3】:

根据this answer,另一种方法可能是

s = string([]rune(s))

Example:

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    s := "...ole\xc5"
    fmt.Println(s, utf8.Valid([]byte(s)))
    // Output: ...ole� false

    s = string([]rune(s))
    fmt.Println(s, utf8.Valid([]byte(s)))
    // Output: ...ole� true
}

即使结果看起来并不“漂亮”,它仍然会将字符串转换为有效的 UTF-8 编码。

【讨论】:

    猜你喜欢
    • 2011-02-09
    • 2018-01-05
    • 2018-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-21
    • 2014-02-12
    • 1970-01-01
    相关资源
    最近更新 更多