Golang有一个很有意思的官方库,叫golang.org/x,x可能是extends,experimental,总之是一些在官方库中没有,但是又很有用的库。最近花点时间把这里有用的介绍一下。
- Golang.org/x库初探1——image库
- Golang.org/x库初探2——text库
text库主要提供strings库的一系列Unicode和国际化适配的替代升级方案。常用的库如下:
golang.org/x/text/language
language库定义了BCP 47的语言标签支持,这里的语言指的自然语言,英语,中文等。
所谓的BCP 47 就是zh-CN这种格式的前面是语言,后面是地域的标识符(地域可选)
language库定义了Match等操作,这样你可以用来匹配最佳的语言,常用的是http中,用户在头部通过Accept-Language标识了他希望使用的语言,这样我们在服务端可以和服务器支持的语言进行匹配,寻找最合适的语言。
这个库虽然介绍的功能主要是Match功能,但同时也是cases等其他库用于标识语言的基础库,所以第一个介绍这个库。当然如果我们要做一个国际化程序,也可以把国际化标识这块,也使用lanuage库作为通用标识,以增强兼容性和标准化。
golang.org/x/text/cases
cases库用于支持国家化的大小写转换,写法如下
var case = cases.Title(language.English, cases.NoLower)
fmt.Print(case.String("beforeThis")) //BeforeThis
这里的Title就是一般的Title case的用法。Title函数支持传入附加Option,上面传入了NoLower参数,用于标识除了首字母之外,其他非小写字母不需要转化为小写。
相比于标准库,cases的具体效果还是有区别,比如 下面的测试例:
func TestCases(t *testing.T) {
var tested = "beforeThis"
fmt.Println("基础库Title:", strings.ToTitle(tested))
var case1 = cases.Title(language.English)
fmt.Println("不带Option的cases Title:", case1.String(tested))
var case2 = cases.Title(language.English, cases.NoLower)
fmt.Println("带Option的cases Title:", case2.String(tested))
}
输出如下:
基础库Title: BEFORETHIS
不带Option的cases Title: Beforethis
带Option的cases Title: BeforeThis
基础库的ToTitle做完全大写转换还是有问题的,cases库就正常一点,对首字母大写即可。
而ToLower和ToUpper表现一致。
而国际化方面,主要针对几种语言做了适应性处理,主要是西欧语言的差异,这里就不多讲了,需要的同学可以从Title函数点进去看代码,有详细的注释
titleInfos = []struct {
title mapFunc
lower mapFunc
titleSpan spanFunc
rewrite func(*context)
}{
{title, lower, isTitle, nil}, // und
{title, lower, isTitle, afnlRewrite}, // af
{aztrUpper(title), aztrLower, isTitle, nil}, // az
{title, lower, isTitle, nil}, // el
{ltUpper(title), ltLower, noSpan, nil}, // lt
{nlTitle, lower, nlTitleSpan, afnlRewrite}, // nl
{aztrUpper(title), aztrLower, isTitle, nil}, // tr
}
golang.org/x/text/number
number库主要定义了很多帮助格式化数字的帮助函数,这个应该我们用的多点,可用的东西包括格式化函数和选项两类,格式化函数包括:
Decimal | 格式化逗号分隔的整数,例如1,234,567这种 |
Engineering | 类科学计数法(但指数部分最多有三位),输出类似这样:12.345678 × 10⁶ |
PerMille | 直接转成千分比数字,例如120‰ |
Percent | 百分比 |
Scientific | 科学计数法,输出类似这样:1.2345678 × 10⁷ |
另外,提供了多个Option来自定义输出:
FormatWidth | 指定数字转换后的字符串宽度,例如 number.PerMille(0.12, number.FormatWidth(10))会输出 “ 120‰” |
IncrementString | 一种宽泛的四舍五入算法,例如 number.PerMille(0.16, number.IncrementString("0.3"))会输出0.3,number.PerMille(0.14, number.IncrementString("0.3"))会输出0.0 |
MaxFractionDigits | 最大小数位数(四舍五入) |
MaxIntegerDigits | 最大整数位数(直接裁剪多余部分) |
MinIntegerDigits | 最小整数位数(补零) |
MinFractionDigits | 最小小数位数(补零) |
NoSeparator | 不分组数字 |
Precision | 浮点数精度,负数为小数位数,正数为整数位数 |
总的用法就是和message包配合,像fmt一样使用,例如官方库的案例
func TestNumber(t *testing.T) {
p := message.NewPrinter(language.Chinese)
p.Printf("%v bottles.\n", number.Decimal(3321.96, number.Precision(2)))
}
其中第二行展示了格式化函数和选项的用法。
这个库我们基本可以在很多需要格式化数字的地方用到了,原来很多很复杂的自定义写法都可以用这个来处理,这个库还提供了从上述标准函数派生自定义的入口NewFormat,大家可以从库中查找使用。
golang.org/x/text/message
上面说了message库提供了一个类似fmt的国际化解决方案,同时可以和number等配合,做更多的格式化以及本地化适应的功能。
但同时他还是一个可以用于多语言翻译的库,在提供国际化方面有很大用处。
例如我们有这么一个帮助:
func TestNumber(t *testing.T) {
pZh := message.NewPrinter(language.Make("zh"))
pEn := message.NewPrinter(language.Make("en"))
message.SetString(language.English, "您输入的%s不符合要求\n", "The input value %s is not legal")
pZh.Printf("您输入的%s不符合要求\n", "1234")
pEn.Printf("您输入的%s不符合要求\n", "1234")
}
=== RUN TestNumber
您输入的1234不符合要求
The input value 1234 is not legal--- PASS: TestNumber (0.00s)
PASS
可以发现,英文的Printer直接转成了英文打印。
但是注意,这个定义必须完全一致才生效,所以官方的例子如下:
message.SetString(language.Dutch, "You have chosen to play %m.", "U heeft ervoor gekozen om %m te spelen.")
message.SetString(language.Dutch, "basketball", "basketbal")
message.SetString(language.Dutch, "hockey", "ijshockey")
message.SetString(language.Dutch, "soccer", "voetbal")
message.SetString(language.BritishEnglish, "soccer", "football")
for _, sport := range []string{"soccer", "basketball", "hockey"} {
for _, lang := range []string{"en", "en-GB", "nl"} {
p := message.NewPrinter(language.Make(lang))
fmt.Printf("%-6s %s\n", lang, p.Sprintf("You have chosen to play %m.", sport))
}
fmt.Println()
}
他首先用了一个Sprintf来把预定义国家化字符串进行转换,再做的Printf。
这个例子里还有一点说明的是,他使用了一个"%m"来说明传入变量要取国际化值,如果改成%s则不转换。
同时,text库提供了一个可编译库gotext用于在代码中获得所有的字符串,用于简化国际化的工作。
golang.org/x/text/encoding
这个库,估计中文环境就用的很多了,就是传说中的编码库,用来做UTF-8,GBK,BIG5转换的,而且他把编码转换的框架进行了标准化,可以做不同编码格式的转换,这样就不需要再去用iconv等麻烦的东西了。
例如encoding下面的简体中文库simplifiedchinese提供了GB18030和GBK两个编码类,两个类都可以使用NewEncoder或NewDecoder来初始化一个编码器或者解码器,例如下面的例子,打开一个utf-8的文件,并转成gbk的:
f, err := os.Open("utftext.txt")
if err == nil {
var out *os.File
out, err = os.Create("gbk.txt")
if err == nil {
//将utf的reader转成gbk的reader
r := simplifiedchinese.GBK.NewDecoder().Reader(f)
//进一步读取拷贝
io.Copy(out, r)
out.Close()
f.Close()
}
}
当然,NewDecoder().Transform()直接按byte数组转换也是可以的。
其他更多
当然,text下还有很多内容,我个人觉得用处不大,就不细讲了,包括
- currency: 货币支持库
- date: 国际化日期相关库
- search:本地化搜索支持,西文中差异较多,中文少
- secure:特殊用处下的文字有效性判断
- runes,collate:utf-8相关库
- unicode: unicode相关库
- transform: 转换库,可配合unicode下的各种定义来做特殊字符移除等功能
- 其他的功能库:feature、width
- 更多的编码库,包括韩文、日文等
详细的可以看官方库介绍
总的我认为:
- 编码转换可以直接用encoding库
- 国际化用message库
- Upper、Lower、Title等转换看需要用cases库或者标准strings库
- number库的功能很强大,在金融等应用场景下用处多多。