猿问

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

我正在转换一个解码电子邮件的 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-8

r := transform.NewReader(strings.NewReader(body), enc.NewDecoder())


// result contains the converted-to-utf8 email body

result, err := ioutil.ReadAll(r)

除非遇到非法字节,否则效果很好,不幸的是,这在野外处理电子邮件时并不少见。ioutil.ReadAll() 返回错误和所有转换的字节,直到出现问题。有没有办法告诉转换包忽略非法字节?现在,我们使用 -c 标志来 iconv 来做到这一点。我已经浏览了转换包的文档,但我不知道这是否可能。


更新: 这是一个显示问题的测试程序(Go 游乐场没有字符集或转换包......)。原始文本取自实际电子邮件。是的,它是英文的,是的,电子邮件中的字符集设置为 EUC-KR。我需要它来忽略那个撇号。


package main


import (

    "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))

}


海绵宝宝撒
浏览 193回答 2
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)这应该过滤掉所有符文错误,然后才能到达阅读器并爆炸。

互换的青春

这是我采用的解决方案。我没有使用 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 (&nbsp; &nbsp; in&nbsp; int&nbsp; &nbsp; out int)for in < len(raw) {&nbsp; &nbsp; // Do the transformation&nbsp; &nbsp; ndst, nsrc, err := d.Transform(dst[out:], []byte(raw[in:]), true)&nbsp; &nbsp; in += nsrc&nbsp; &nbsp; out += ndst&nbsp; &nbsp; if err == nil {&nbsp; &nbsp; &nbsp; &nbsp; // Completed transformation&nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; }&nbsp; &nbsp; if err == transform.ErrShortDst {&nbsp; &nbsp; &nbsp; &nbsp; // Our output buffer is too small, so we need to grow it&nbsp; &nbsp; &nbsp; &nbsp; log.Printf("Short")&nbsp; &nbsp; &nbsp; &nbsp; t := make([]byte, (cap(dst)+1)*2)&nbsp; &nbsp; &nbsp; &nbsp; copy(t, dst)&nbsp; &nbsp; &nbsp; &nbsp; dst = t&nbsp; &nbsp; &nbsp; &nbsp; continue&nbsp; &nbsp; }&nbsp; &nbsp; // We're here because of at least one illegal character. Skip over the current rune&nbsp; &nbsp; // and try again.&nbsp; &nbsp; _, width := utf8.DecodeRuneInString(raw[in:])&nbsp; &nbsp; in += width}
随时随地看视频慕课网APP

相关分类

Go
我要回答