编码转换


不论从哪个面向,都可以看出 Go 独厚 UTF-8,这可能是因为 Go 的设计者之一 Ken Thompson,也曾经参与了 UTF-8 的设计。

如果文本数据的来源并非 UTF-8 呢?例如,存储时并非使用 UTF-8 的文件?解决的方法之一,是将文件另行存储为 UTF-8,再使用 Go 来读取,当然,并非所有的的场合都可以这么做,另一个方式是,使用golang.org/x/text包。

Go 除了本身自带的标准包之外,还有另外一系列官方的扩充包,这些扩充包组织在golang/x底下,依文件上的说明,这些包也是 Go 项目的一部份,只不过在兼容性的维护上比较没那么严格。

在官方扩充包中,golang.org/x/text主要包含了文本编码、转换、国际化、本地化等文本性任务的包,可以透过go get来获取、安装:

go get golang.org/x/text

文本编码的转换主要由golang.org/x/text/transform包来处理,看看其中的函数或结构方法,都会需要Transformer接口的实现,例如最基本的String

func String(t Transformer, s string) (result string, n int, err error)

Transformer定义的主要是Transform方法,代表着编码的转换:

type Transformer interface {
    Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error)
    Reset()
}

dstsrc代表着同一文本两个不同编码的字节,由于 Go 使用 UTF-8,从 UTF-8 转换为其他编码,这个动作称为Encode,从其他编码转换为 UTF-8,这个动作称为 Decode。

Encode、Decode 的动作,分别由golang.org/x/text/encoding包的EncoderDecoder来处理,它们都是transform.Transformer的实现:

type Encoder struct {
    transform.Transformer
    ...
}

type Decoder struct {
    transform.Transformer
    ...
}

为了便于使用,encoding定义了Encoding的行为:

type Encoding interface {
    NewDecoder() *Decoder
    NewEncoder() *Encoder
}

golang.org/x/text/encoding包之中,定义了不同的编码转换包,例如,想处理 Big5(Code Page 950) 编码转换的话,需要golang.org/x/text/encoding/traditionalchinese包,它的Big5就实现了Encoding,因此想要获得 UTF-8 Big5 的EncoderDecoder,可以如下:

utf8ToBig5 := traditionalchinese.Big5.NewEncoder()
big5ToUtf8 := traditionalchinese.Big5.NewDecoder()

因此,若要读取一个底层为 Big5 编码的字符串,转换为 UTF-8 编码字符串,可以如下:

package main

import (
    "golang.org/x/text/encoding/traditionalchinese"
    "golang.org/x/text/transform"
    "fmt"
)

func main() {
    big5ToUTF8 := traditionalchinese.Big5.NewDecoder()
    big5Test := "\xb4\xfa\xb8\xd5" // 测试的 Big5 编码
    utf8, _, _ := transform.String(big5ToUTF8, big5Test)
    fmt.Println(utf8) // 显示「测试」
}

要将一个 UTF-8 编码字符串,转换为 Big5 编码的字符串,可以如下:

package main

import (
    "golang.org/x/text/encoding/traditionalchinese"
    "golang.org/x/text/transform"
    "fmt"
)

func main() {
    utf8ToBig5 := traditionalchinese.Big5.NewEncoder()
    big5, _, _ := transform.String(utf8ToBig5, "测试")
    fmt.Printf("%q", big5)  // 显示 "\xb4\xfa\xb8\xd5"
}

transform也定义了ReaderWriter,可以用来将Transformerio.Readerio.Writer包裹在一起:

type Reader
    func NewReader(r io.Reader, t Transformer) *Reader
    func (r *Reader) Read(p []byte) (int, error)

type Writer
    func NewWriter(w io.Writer, t Transformer) *Writer
    func (w *Writer) Close() error
    func (w *Writer) Write(data []byte) (n int, err error)

例如,想要读取 Big5 文件的话,底下是个示范:

package main

import (
    "golang.org/x/text/encoding/traditionalchinese"
    "golang.org/x/text/transform"
    "fmt"
    "io"
    "os"
    "io/ioutil"
)

func printBig5(r io.Reader) error {
    var big5R = transform.NewReader(r, traditionalchinese.Big5.NewDecoder())

    b, err := ioutil.ReadAll(big5R)
    fmt.Println(string(b))

    return err
}

func main() {
    fmt.Print("文件来源:")
    var filename string
    fmt.Scanf("%s", &filename)

    f, err := os.Open(filename)
    if err != nil {
        panic(err)
    }
    defer f.Close()

    printBig5(f)
}




展开阅读全文