Golang中的字符编码(Encoding)

1 字符编码详解

在web应用开发中,我们经常会遇到编、解码、序列化和反序列化概念。所谓的编解码,都是针对字符而言的。下面对编码知识做一下整理总结。知识来源参见引用1 2 3 4

1.1 字符集与字符编码

字符是各种文字和符号的总称,包括各个国家文字、标点符号、图形符号、数字等。

字符集是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集有:ASCII字符集、ISO 8859字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。计算机要准确的处理各种字符集文字,需要进行字符编码,以便计算机能够识别和存储各种文字。

字符集只是字符的集合,不一定适合作网络传送、处理,有时须经编码(encode)后才能应用。如Unicode可依不同需要以UTF-8、UTF-16、UTF-32等方式编码。字符编码就是以二进制的数字来对应字符集的字符。因此,对字符进行编码,是信息交流的技术基础。

各个国家和地区在制定编码标准的时候,“字符的集合”和“编码”一般都是同时制定的。因此,平常我们所说的“字符集”,比如:GB2312, GBK, JIS 等,除了有“字符的集合”这层含义外,同时也包含了“编码”的含义。

注意:Unicode字符集有多种编码方式,如UTF-8、UTF-16等;ASCII只有一种;大多数MBCS(包括GB2312)也只有一种。

1.2 三种国际标准编码

1.2.1 ASCII编码

ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语,而其扩展版本EASCII则可以勉强显示其他西欧语言。它是现今最通用的单字节编码系统(但是有被UniCode追上的迹象),并等同于国际标准ISO/IEC 646。 ASCII第一次以规范标准的型态发表是在1967年,最后一次更新则是在1986年,至今为止共定义了128个字符;其中33个字符无法显示(这是以现今操作系统为依归,但在DOS模式下可显示出一些诸如笑脸、扑克牌花式等8-bit符号),且这33个字符多数都已是陈废的控制字符。控制字符的用途主要是用来操控已经处理过的文字。在33个字符之外的是95个可显示的字符,包含用键盘敲下空白键所产生的空白字符也算1个可显示字符(显示为空白)。 ASCII表:见http://zh.wikipedia.org/zh-cn/ASCII

ASCII缺点: ASCII的最大缺点是只能显示26个基本拉丁字母、阿拉伯数目字和英式标点符号,因此只能用于显示现代美国英语(而且在处理英语当中的外来词如naïve、café、élite等等时,所有重音符号都不得不去掉,即使这样做会违反拼写规则)。而EASCII虽然解决了部份西欧语言的显示问题,但对更多其他语言依然无能为力。因此现在的苹果电脑已经抛弃ASCII而转用Unicode。

最早的英文DOS操作系统的系统内码是:ASCII。计算机这时候只支持英语,其他语言不能够在计算机存储和显示。

在该阶段,单字节字符串使用一个字节存放一个字符(SBCS,Single Byte Character System)。如:”Bob123”占6个字节。

1.2.2 ANSI编码

首先,ANSI并不是某一种特定的字符编码,而是在不同的系统中,ANSI表示不同的编码。下述文字阐述比较清楚,来自引用5

其实ANSI并不是某一种特定的字符编码,而是在不同的系统中,ANSI表示不同的编码。你的美国同事Bob的系统中ANSI编码其实是ASCII编码(ASCII编码不能表示汉字,所以汉字为乱码),而你的系统中(“汉字”正常显示)ANSI编码其实是GBK编码,而韩文系统中(“한국어”正常显示)ANSI编码其实是EUC-KR编码。

话说计算机是由美国佬搞出来的嘛,他们觉得一个字节(可以表示256个编码)表示英语世界里所有的字母、数字和常用特殊符号已经绰绰有余了(其实ASCII只用了前127个编码)。后来欧洲人不干了,法国人说:我需要在小写字母加上变音符号(如:é),德国人说:我也要加几个字母(Ä ä、Ö ö、Ü ü、ß)。于是,欧洲人就将ASCII没用完的编码(128-255)为自己特有的符号编码(后来称之为“扩展字符集”)。等到我们中国人开始使用计算机的时候,尼玛,256个编码哪够?我泱泱大中华,汉字起码也得N多万吧,就连小学生都得要求掌握两三千字。国标局最后拍板:一个字节不够,那我们就用多个字节来为汉字编码吧,但是,国情那么穷,字节那么贵,三个字节伤不起,那就用俩字节吧,先给常用的几千汉字编个码,等以后国家强盛了人民富裕了,咱再扩展呗—于是GB2312就产生了。台湾同胞一看,尼玛,全是简体字,还让不让我们写繁体字的活了,于是台湾同胞也自己弄了个繁体字编码—大五码(Big-5)。同时,其它国家也在为自己的文字编码。最后,微软苦逼了:顾客就是上帝啊,你们的编码我都得满足啊,这样吧,卖给美国国内的系统默认就用ASCII编码吧,卖给中国人的系统默认就用GBK编码吧,卖给韩国人的系统默认就用EUC-KR编码,…但是为了避免你们误会我卖给你们的系统功能有差异,我就统一把你们的默认编码都显示成ANSI吧。—本故事纯属虚构,但“ANSI编码”确实只存在于Windows系统。

那么Windows系统是如何区分ANSI背后的真实编码的呢?

微软用一个叫“Windows code pages”6(在命令行下执行chcp命令可以查看当前code page的值)的值来判断系统默认编码,比如:简体中文的code page值为936(它表示GBK编码,win95之前表示GB2312,详见:Microsoft Windows’ Code Page 936),繁体中文的code page值为950(表示Big-5编码)。

我们能否通过修改Windows code pages的值来改变“ANSI编码”呢?

命令提示符下,我们可以通过chcp命令来修改当前终端的active code page,例如:

(1) 执行:chcp 437,code page改为437,当前终端的默认编码就为ASCII编码了(汉字就成乱码了);

(2) 执行:chcp 936,code page改为936,当前终端的默认编码就为GBK编码了(汉字又能正常显示了)。

上面的操作只在当前终端起作用,并不会影响系统默认的“ANSI编码”。(更改命令行默认codepage参看:设置cmd的codepage的方法)。

Windows下code page是根据当前系统区域(locale)来设置的,要想修改系统默认的“ANSI编码”,我们可以通过修改系统区域来实现(“控制面板” =>“时钟、语言和区域”=>“区域和语言”=>“管理”=>“更改系统区域设置…”):

系统locale为简体中文,意味着当前“ANSI编码”实际是GBK编码。当你把它改成Korean(Korea)时,“ANSI编码”实际是EUC-KR编码,“한국어”就能正常显示了;当你把它改成English(US)时,“ANSI编码”实际是ASCII编码,“汉字”和“한국어”都成乱码了。(改了之后需要重启系统的。。。)

你上面说的都是windows的情形吧,Linux呢?

将前述内容为“汉字”的文件test.txt拷贝至Linux下,用Emacs打开发现也是乱码。

运行<locale>发现系统编码默认为UTF-8。运行<export LC_ALL=ZH_CN.GBK> 就能正常显示了。

1.2.3 UNICODE编码

Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案7 。目前的Unicode字符分为17组编排,0x0000 至 0x10FFFF,每组称为平面(Plane),而每平面拥有65536个码位,共1114112个。然而目前只用了少数平面。UTF-8、UTF-16、UTF-32都是将数字转换到程序数据的编码方案。

(1)编码标准:UTF-8, UTF-16, UnicodeBig。

(2)与“ANSI 编码”类似的,把字符串通过 UNICODE 编码转化成“字节串”时,一个 UNICODE 字符可能转化成一个字节或多个字节。

与“ANSI 编码”不同的是:

(1)这些“UNICODE 编码”能够处理所有的 UNICODE 字符。

(2)“UNICODE 字符”与“转换出来的字节”之间是可以通过计算得到的。

Unicode字符集包含了各种语言中使用到的所有“字符”。用来给 UNICODE 字符集编码的标准有很多种,比如:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等。 符号对应表,可以查询unicode.org8,或者专门的汉字对应表9

1.3 Unicode编码实现

对于Unicode,字符集和编码是明确区分的,用Unicode来称呼一个编码方案不合适。Unicode/UCS 标准首先是个统一的字符集标准。而 Unicode/UCS 标准同时也定义了几种可选的编码方案,在标准文档中称作「encoding form」,主要包括 UTF-8、UTF-16 和 UTF-32。 所以,对Unicode 方案来说,同样的基于Unicode 字符集的文本可以用多种编码来存储、传输。

Unicode是内存编码表示方案(是规范),而UTF是如何保存和传输Unicode的方案(是实现)。

1.3.1 UTF-8

UCS-2编码(16进制) UTF-8 字节流(二进制)

0000 - 007F 0xxxxxxx

0080 - 07FF 110xxxxx 10xxxxxx

0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx

例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001,用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。

可见UTF-8是变长的,将Unicode编码为00000000-0000007F的字符,用单个字节来表示; 00000080-000007FF的字符用两个字节表示;00000800-0000FFFF的字符用3字节表示。因为目前为止Unicode-16规范没有指定FFFF以上的字符,所以UTF-8最多是使用3个字节来表示一个字符。但理论上来说,UTF-8最多需要用6字节表示一个字符。

UTF-8兼容ASCII。

1.3.2 UTF-16

标准的Unicode称为UTF-16。

UTF-16和上面提到的Unicode本身的编码规范是一致的。

UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码,定义了一个算法。不过由于实际使用的UCS2,或者UCS4的BMP必然小于0x10000,所以就目前而言,可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节序的问题。

UTF-16不兼容ASCII。

1.3.3 UTF-7

UTF-7 (7-位元 Unicode 转换格式(Unicode Transformation Format,简写成 UTF)) 是一种可变长度字元编码方式,用以将 Unicode 字元以 ASCII 编码的字元串来呈现,可以应用在电子邮件传输之类的应用。

UTF-7并非Unicode标准之一。

1.3.4 UTF的字节序

UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?

Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一个有点小聪明的想法:

在UCS编码中有一个叫做”ZERO WIDTH NO-BREAK SPACE”的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符”ZERO WIDTH NO-BREAK SPACE”。

这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符”ZERO WIDTH NO-BREAK SPACE”又被称作BOM。

UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符”ZERO WIDTH NO-BREAK SPACE”的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了

1.3.4 UTF的BOM

为了识别 Unicode 文件,Microsoft 建议所有的 Unicode 文件应该以 ZERO WIDTH NOBREAK SPACE(U+FEFF)字符开头。这作为一个“特征符”或“字节顺序标记(byte-order mark,BOM)”来识别文件中使用的编码和字节顺序

UTF-8文件的BOM是“EF BB BF”,但是UTF-8的字节顺序是不变的,因此这个文件头实际上不起作用。有一些编程语言是ISO-8859-1编码,所以如果用UTF-8针对这些语言编程序,就必须去掉BOM,即保存成“UTF-8—无BOM”的格式才可以,PHP语言就是这样。

因为一些系统或程序不支持BOM,因此带有BOM的Unicode文件有时会带来一些问题。

JDK1.5以及之前的Reader都不能处理带有BOM的UTF-8编码的文件,解析这种格式的xml文件时,会抛出异常:Content is not allowed in prolog。“对于解决方法,之后我会写篇文章专门讨论该问题。”

Linux/UNIX 并没有使用 BOM,因为它会破坏现有的 ASCII 文件的语法约定。

不同的编辑工具对BOM的处理也各不相同。使用Windows自带的记事本将文件保存为UTF-8编码的时候,记事本会自动在文件开头插入BOM(虽然BOM对UTF-8来说并不是必须的)。而其它很多编辑器用不用BOM是可以选择的。UTF-8、UTF-16都是如此。

涉及跨平台兼容性时,不要用Windows记事本,用专业的文本编辑器保存为不带 BOM 的 UTF-8。

1.3.5 Windows记事本关于Unicode的几个方言

(1)ANSI编码

记事本默认保存的编码格式是:ANSI,即本地操作系统默认的内码,简体中文一般为GB2312。这个怎么验证呢?用记事本保存后,使用EmEditor、EditPlus和UltraEdit之类的文本编辑器打开。推荐使用EmEditor,打开后,在又下角会显示编码:GB2312。

(2)Unicode编码

用记事本另存为时,编码选择“Unicode”,用EmEditor打开该文件,发现编码格式是:UTF-16LE+BOM(有签名)。用十六进制方式查看,发现开头两字节为:FF FE。这就是BOM。

把带有BOM的小端序UTF-16称作Unicode是Windows记事本语境里的一个方言。

(3)Unicode big endian

用记事本另存为时,编码选择“Unicode”,用EmEditor打开该文件,发现编码格式是:UTF-16BE+BOM(有签名)。用十六进制方式查看,发现开头两字节为:FE FF。这就是BOM。

(4)UTF-8

用记事本另存为时,编码选择“UTF-8”,用EmEditor打开该文件,发现编码格式是:UTF-8(有签名)。用十六进制方式查看,发现开头三个字节为:EF BB BF。这就是BOM。

把带 BOM 的UTF-8称作UTF-8是Windows记事本语境里的一个方言。

1.4 几种中文编码

1.4.1 GB2312

10当中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存,于是想到把那些ASCII码中127号之后的奇异符号们直接取消掉, 规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了。这种汉字方案叫做 “GB2312”。GB2312 是对 ASCII 的中文扩展,兼容ASCII。

1.4.2 GBK

11 但是中国的汉字太多了,我们很快就就发现有许多人的人名没有办法在这里打出来,不得不继续把 GB2312 没有用到的码位找出来用上。后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 “GBK” 标准,GBK 包括了 GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。

1.4.3 GB18030

后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK 扩成了 GB18030。从此之后,中华民族的文化就可以在计算机时代中传承了。

1.4.4 BIG5

大五码(Big5),是通行于台湾、香港地区的一个繁体字编码方案。地区标准号为:CNS11643,这就是人们讲的BIG-5码。

1.5 字符工具推荐

16进制查看和编辑软件 winhex UltraEdit

2 Golang字符编码处理

Golang代码本身是UTF-8的,官方只提供了Unicode字符编码包。

Golang的源码文件、模版文件等,程序的读取和打开方式也都使用UTF-8编码。

2.1 Centos环境确保Golang代码显示和编辑正确的设置

vim编码设置 <vim ~/.vimrc>

set encoding=utf-8
set fileencodings=utf-8,ucs-bom,cp936,gb18030,latin1
set termencoding=utf-8
set expandtab
set ts=4
set shiftwidth=4
syntax on

if has('mouse')
set mouse-=a
endif

NotePad++ 选择使用utf-8不带BOM

为确保远程管理工具屏幕显示为UTF-8编码,设置SecureCRT 选择使用UTF-8

2.2 处理其他字符编码的第三方库

参考12

code.google.com/p/go.text/encoding

code.google.com/p/go.text/transform

还有以下是一些依赖iconv c库的开源字符集转换库:

iconv-go,通过cgo封装了iconv库;qiniu iconv,同样通过cgo封装iconv库;go-charset,支持UTF-8转换为其他字符集(非iconv库),同时也封装了iconv,提供更多字符集的转换。 这些在linux 上用用还好,到了windows 下要装mingw,而且又有32位和64位的区别,比较烦,所以个人不喜欢

另外还有一个Mahonia—a character-set conversion library for Go,完全go实现,但是这个库已经停止维护了。

如果没有特别的要求,个人还是建议使用 code.google.com/p/go.text/encoding

这个库要用hg来安装,记得先安装python 2.7 和Mercurial。

2.3 标准包encoding中的几个编码子包

Ascii85 、Base32和Base64同属于一种二进制到文本(binary-to-text)编码。

2.3.1 Ascii85包

Ascii85也被称为Base85v13 14它使用5个ASCII字符来表示4个byte数据,被认为编码效率比Base64高。经常被用作一种btoa工具,也用于Adobe PostScript和PDF文档式。

应用代码:<ascii85_test.go>

package encoding

import (
    "encoding/ascii85"
    "testing"
)

func Test_ascii85(t *testing.T) {
    src := "hello world"
    srcbyte := []byte(src)
    var dst []byte = make([]byte, ascii85.MaxEncodedLen(len(srcbyte)))
    ascii85.Encode(dst, []byte(src))
    t.Log(string(dst))
    // output:
    // BOu!rD]j7BEbo7d
}

2.3.1 Base32和Base64包

Base64是一种用64个Ascii来表示byte数据的一种编码,它设计的初衷15被用来在储存和传输数据的时候兼容一些历史原因,通常这些历史设备或装置只能使用Ascii码。后来,Base64也在一些应用中被广泛应用16:比如储存和传输公钥证书、XML中储存复杂数据、HTTP的URL和Request数据编码、一些特殊情况下的图片传送等。

应用代码:<base64_test.go>

package encoding

import (
    "encoding/base64"
    "testing"
)

func Test_base64(t *testing.T) {
    src := "hello world"
    srcbyte := []byte(src)
    dst := base64.StdEncoding.EncodeToString(srcbyte)
    t.Log(dst)
    // output:
    // aGVsbG8gd29ybGQ=

    decodebytes, _ := base64.StdEncoding.DecodeString(dst)
    t.Log(string(decodebytes))
    // output:
    // hello world
}

2.3 标准包unicode

unicode包17提供了对Unicode字符的一些操作。详解参照18

2.3.1 主要类型

2.3.2 主要变量Variables

首先,定义并初始化了所有的Script19结构体,这些变量中都包含*RangeTable类型。比如unicode.Han。 还有以下map和数组类型的对象

var Categories = map[string]*RangeTable{ ... }  //对应Unicode的category表(通用类别表)
var FoldCategory = map[string]*RangeTable{ ... }
var FoldScript = map[string]*RangeTable{}
var GraphicRanges = []*RangeTable{ ... }
var PrintRanges = []*RangeTable{
var Properties = map[string]*RangeTable{ ... } //对应Unicode字符属性表
var Scripts = map[string]*RangeTable{ ... }  //对应Unicode的script表 

Unicode的Category表(通用类别表)用来表示字符类型,比如Lu大写、Ll小写、Lt词首字母大写、Sc货币等,大概40个。

Unicode的Script表20。 Script是一个字符和可书写符号的集合,有些Script只支持一种书写系统或语言,有些Script支持多种语言,比如Latin script支持English, French, German, Italian, Vietnamese。

Unicode的Property 21概念待研究。

2.3.2 主要方法

字符判断方法:通常用来判断字符属于哪种类别、属性或是否在某个Script表。

func In(r rune, ranges ...*RangeTable) bool
func Is(rangeTab *RangeTable, r rune) bool  //判断字符 r 是否在 rangtable范围内,可用来判断是否汉字
func IsControl(r rune) bool     //判断 r 是否为一个控制字符,类别 C 包含更多字符,比如代理字符
func IsDigit(r rune) bool       //判断 r 是否为一个十进制的数字字符
func IsGraphic(r rune) bool     //判断字符 r 是否为一个“图形字符”,“图形字符”包括字母、标记、数字、标点、符号、空格,他们分别对应于 L、M、N、P、S、Zs 类别
func IsLetter(r rune) bool      //判断 r 是否为一个字母字符 (类别 L) 汉字也是一个字母字符
func IsLower(r rune) bool       //判断字符 r 是否为小写格式
func IsMark(r rune) bool        //判断 r 是否为一个 mark 字符 (类别 M)
func IsNumber(r rune) bool      //判断 r 是否为一个数字字符 (类别 N) 
func IsOneOf(ranges []*RangeTable, r rune) bool     //判断 r 是否在 set 范围内
func IsPrint(r rune) bool       //IsPrint 判断字符 r 是否为 Go 所定义的“可打印字符”,可打印字符”包括字母、标记、数字、标点、符号和 ASCII 空格,,他们分别对应于 L, M, N, P, S 类别和 ASCII 空格
func IsPunct(r rune) bool       //判断 r 是否为一个标点字符 (类别 P)
func IsSpace(r rune) bool       //判断 r 是否为一个空白字符, Latin-1 字符集中,空白字符为:\t, \n, \v, \f, \r,空格, U+0085 (NEL), U+00A0 (NBSP),其它空白字符的定义有“类别 Z”和“Pattern_White_Space 属性”
func IsSymbol(r rune) bool      //判断 r 是否为一个符号字符
func IsTitle(r rune) bool       //判断字符 r 是否为 Unicode 规定的 Title 字符,大部分字符的 Title 格式就是其大写格式
func IsUpper(r rune) bool       //判断字符 r 是否为大写格式

// 示例:
for _, r := range "Hello 世界!" {
    // 判断字符是否为汉字
    if unicode.Is(unicode.Scripts["Han"], r) {
        fmt.Printf("%c", r) // 世界
    }
}   
for _, r := range "Hello ABC!" {
    // 判断字符是否为大写
    if unicode.IsUpper(r) {
        fmt.Printf("%c", r) // HABC
    }
}
for _, r := range "Hello abc!" {
    // 判断字符是否为小写
    if unicode.IsLower(r) {
        fmt.Printf("%c", r) // elloabc
    }
}
for _, r := range "Hello ᾏᾟᾯ!" {
    // 判断字符是否为标题
    if unicode.IsTitle(r) {
        fmt.Printf("%c", r) // ᾏᾟᾯ
    }
}

//判断字符是否为汉字和标点
// 将 set 设置为“汉字、标点符号”
set := []*unicode.RangeTable{unicode.Han, unicode.P}
for _, r := range "Hello 世界!" {
    if unicode.IsOneOf(set, r) {
        fmt.Printf("%c", r)
    }
} // 世界!

字符判断方法:通常用来判断字符属于哪种类别、属性或是否在某个Script表。

func To(_case int, r rune) rune // To 将字符 r 转换为指定的格式,case 取值:UpperCase、LowerCase、TitleCase
func ToLower(r rune) rune   // ToLower 将字符 r 转换为小写格式
func ToTitle(r rune) rune   // ToTitle 将字符 r 转换为 Title 格式
func ToUpper(r rune) rune   // ToUpper 将字符 r 转换为大写格式

参考文献:


  1. 知乎 https://www.zhihu.com/question/20650946 [return]
  2. polaris http://polaris.blog.51cto.com/1146394/377468/ [return]
  3. 阮一峰 http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html [return]
  4. 字符,字节和编码 http://www.regexlab.com/zh/encoding.htm [return]
  5. alecrab http://www.cnblogs.com/malecrab/p/5300486.html [return]
  6. CodePage https://msdn.microsoft.com/en-us/library/windows/desktop/dd317756(v=vs.85).aspx [return]
  7. 百度百科 http://baike.baidu.com/link?url=S5a_MPeKIGeZ40i8joVpO0mjPa3z19JWjVmXxa8BkF7U9iMXHn0mCTNKL5H87C_tTd_lVVKrWW4oAWoJsK12DK [return]
  8. Unicode官网 http://www.unicode.org/ [return]
  9. Unicode汉字对照表 http://www.chi2ko.com/tool/CJK.htm [return]
  10. GB2312编码表 http://www.knowsky.com/resource/gb2312tbl.htm [return]
  11. GBK编码表 http://www.qqxiuzi.cn/zh/hanzi-gbk-bianma.php [return]
  12. Golang字符编码第三方库 http://www.cnblogs.com/lyqf365/p/3739533.html [return]
  13. Ascii85 wikipedia https://en.wikipedia.org/wiki/Ascii85 [return]
  14. golang ascii85 https://golang.org/pkg/encoding/ascii85/ [return]
  15. rfc4648 https://tools.ietf.org/html/rfc4648 [return]
  16. 知乎 https://www.zhihu.com/question/36306744 [return]
  17. golang unicode包 https://golang.org/pkg/unicode/ [return]
  18. golove博客 http://www.cnblogs.com/golove/p/3273585.html [return]
  19. Script (Unicode) https://en.wikipedia.org/wiki/Script_(Unicode) [return]
  20. Unicode 9.0 Character Code Charts http://www.unicode.org/charts/index.html [return]
  21. Unicode character property https://en.wikipedia.org/wiki/Unicode_character_property [return]