为了账号安全,请及时绑定邮箱和手机立即绑定

使用 Go 解码文本时忽略非法字节?

使用 Go 解码文本时忽略非法字节?

Go
海绵宝宝撒 2021-11-08 14:40:00
我正在转换一个解码电子邮件的 Go 程序。它目前运行 iconv 来进行实际解码,这当然有开销。我想使用golang.org/x/text/transform和golang.org/x/net/html/charset包来做到这一点。这是工作代码:// cs is the charset that the email body is encoded with, pulled from// the Content-Type declaration.enc, name := charset.Lookup(cs)if enc == nil {    log.Fatalf("Can't find %s", cs)}// body is the email body we're converting to utf-8r := transform.NewReader(strings.NewReader(body), enc.NewDecoder())// result contains the converted-to-utf8 email bodyresult, err := ioutil.ReadAll(r)除非遇到非法字节,否则效果很好,不幸的是,这在野外处理电子邮件时并不少见。ioutil.ReadAll() 返回错误和所有转换的字节,直到出现问题。有没有办法告诉转换包忽略非法字节?现在,我们使用 -c 标志来 iconv 来做到这一点。我已经浏览了转换包的文档,但我不知道这是否可能。更新: 这是一个显示问题的测试程序(Go 游乐场没有字符集或转换包......)。原始文本取自实际电子邮件。是的,它是英文的,是的,电子邮件中的字符集设置为 EUC-KR。我需要它来忽略那个撇号。package mainimport (    "io/ioutil"    "log"    "strings"    "golang.org/x/net/html/charset"    "golang.org/x/text/transform")func main() {    raw := `So, at 64 kBps, or kilobits per second, you’re getting 8 kilobytes a second.`    enc, _ := charset.Lookup("euc-kr")    r := transform.NewReader(strings.NewReader(raw), enc.NewDecoder())    result, err := ioutil.ReadAll(r)    if err != nil {        log.Printf("ReadAll returned %s", err)    }    log.Printf("RESULT: '%s'", string(result))}
查看完整描述

2 回答

?
湖上湖

TA贡献2003条经验 获得超2个赞

enc.NewDecoder()结果是transform.Transformer. 的文档NewDecoder()说:


转换不属于该编码的源字节本身不会导致错误。每个无法转码的字节将在输出中由 '\uFFFD' 的 UTF-8 编码表示,替换符文。


这告诉我们是读取器在替换符文(也称为错误符文)上失败了。幸运的是,很容易将它们剥离出来。


golang.org/x/text/transform提供了两个辅助函数我们可以用来解决这个问题。Chain()需要一组变压器并将它们链接在一起。RemoveFunc()接受一个函数并过滤掉它返回 true 的所有字节。


类似以下(未经测试)的东西应该可以工作:


filter := transform.Chain(enc.NewDecoder(), transform.RemoveFunc(func (r rune) bool {

    return r == utf8.RuneError

}))

r := transform.NewReader(strings.NewReader(body), filter)

这应该过滤掉所有符文错误,然后才能到达阅读器并爆炸。


查看完整回答
反对 回复 2021-11-08
?
互换的青春

TA贡献1797条经验 获得超6个赞

这是我采用的解决方案。我没有使用 Reader,而是手动分配目标缓冲区并Transform()直接调用该函数。当Transform()错误出现时,我会检查一个短的目标缓冲区,并在必要时重新分配。否则我跳过一个符文,假设它是非法字符。为完整起见,我还应该检查短输入缓冲区,但在本例中我没有这样做。


raw := `So, at 64 kBps, or kilobits per second, you’re getting 8 kilobytes a second.`

enc, _ := charset.Lookup("euc-kr")

dst := make([]byte, len(raw))

d := enc.NewDecoder()


var (

    in  int

    out int

)

for in < len(raw) {

    // Do the transformation

    ndst, nsrc, err := d.Transform(dst[out:], []byte(raw[in:]), true)

    in += nsrc

    out += ndst

    if err == nil {

        // Completed transformation

        break

    }

    if err == transform.ErrShortDst {

        // Our output buffer is too small, so we need to grow it

        log.Printf("Short")

        t := make([]byte, (cap(dst)+1)*2)

        copy(t, dst)

        dst = t

        continue

    }

    // We're here because of at least one illegal character. Skip over the current rune

    // and try again.

    _, width := utf8.DecodeRuneInString(raw[in:])

    in += width

}


查看完整回答
反对 回复 2021-11-08
  • 2 回答
  • 0 关注
  • 192 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信