不论从哪个面向,都可以看出 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()
}
dst
、src
代表着同一文本两个不同编码的字节,由于 Go 使用 UTF-8,从 UTF-8 转换为其他编码,这个动作称为Encode
,从其他编码转换为 UTF-8,这个动作称为 Decode。
Encode、Decode 的动作,分别由golang.org/x/text/encoding
包的Encoder
、Decoder
来处理,它们都是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 的Encoder
、Decoder
,可以如下:
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
也定义了Reader
、Writer
,可以用来将Transformer
与io.Reader
、io.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)
}