Go語(yǔ)言類(lèi)型轉(zhuǎn)換工具庫(kù)cast的實(shí)現(xiàn)
簡(jiǎn)介
cast可以在 Go 中輕松安全地從一種類(lèi)型轉(zhuǎn)換為另一種類(lèi)型,cast 提供了簡(jiǎn)單的函數(shù)來(lái)輕松地將數(shù)字轉(zhuǎn)換為字符串,將接口轉(zhuǎn)換為布爾值等。當(dāng)需要顯示類(lèi)型轉(zhuǎn)換時(shí),cast 會(huì)智能地執(zhí)行轉(zhuǎn)換操作。
快速入門(mén)
安裝
go get github.com/spf13/cast
使用
package main
import (
"fmt"
"github.com/spf13/cast"
)
func main() {
// ToString
fmt.Println(cast.ToString("leedarjun")) // leedarjun
fmt.Println(cast.ToString(8)) // 8
fmt.Println(cast.ToString(8.31)) // 8.31
fmt.Println(cast.ToString([]byte("one time"))) // one time
fmt.Println(cast.ToString(nil)) // ""
var foo interface{} = "one more time"
fmt.Println(cast.ToString(foo)) // one more time
// ToInt
fmt.Println(cast.ToInt(8)) // 8
fmt.Println(cast.ToInt(8.31)) // 8
fmt.Println(cast.ToInt("8")) // 8
fmt.Println(cast.ToInt(true)) // 1
fmt.Println(cast.ToInt(false)) // 0
var eight interface{} = 8
fmt.Println(cast.ToInt(eight)) // 8
fmt.Println(cast.ToInt(nil)) // 0
}
實(shí)際上,cast實(shí)現(xiàn)了多種常見(jiàn)類(lèi)型之間的相互轉(zhuǎn)換,返回最符合直覺(jué)的結(jié)果。例如:
nil轉(zhuǎn)為string的結(jié)果為"",而不是"nil";true轉(zhuǎn)為string的結(jié)果為"true",而true轉(zhuǎn)為int的結(jié)果為1;interface{}轉(zhuǎn)為其他類(lèi)型,要看它里面存儲(chǔ)的值類(lèi)型。
這些類(lèi)型包括所有的基本類(lèi)型(整形、浮點(diǎn)型、布爾值和字符串)、空接口、nil,時(shí)間(time.Time)、時(shí)長(zhǎng)(time.Duration)以及它們的切片類(lèi)型,還有map[string]Type(其中Type為前面提到的類(lèi)型):
byte bool float32 float64 string
int8 int16 int32 int64 int
uint8 uint16 uint32 uint64 uint
interface{} time.Time time.Duration nil
高級(jí)轉(zhuǎn)換
cast提供了兩組函數(shù):
ToType(其中Type可以為任何支持的類(lèi)型),將參數(shù)轉(zhuǎn)換為Type類(lèi)型。如果無(wú)法轉(zhuǎn)換,返回Type類(lèi)型的零值或nil;ToTypeE以 E 結(jié)尾,返回轉(zhuǎn)換后的值和一個(gè)error。這組函數(shù)可以區(qū)分參數(shù)中實(shí)際存儲(chǔ)了零值,還是轉(zhuǎn)換失敗了。
實(shí)現(xiàn)上大部分代碼都類(lèi)似,ToType在內(nèi)部調(diào)用ToTypeE函數(shù),返回結(jié)果并忽略錯(cuò)誤。ToType函數(shù)的實(shí)現(xiàn)在文件cast.go中,而ToTypeE函數(shù)的實(shí)現(xiàn)在文件caste.go中。
部分源碼如下:
// ToBoolE casts an interface to a bool type.
func ToBool(i interface{}) bool {
v, _ := ToBoolE(i)
return v
}
// ToDuration casts an interface to a time.Duration type.
func ToDuration(i interface{}) time.Duration {
v, _ := ToDurationE(i)
return v
}
ToTypeE函數(shù)都接受任意類(lèi)型的參數(shù)(interface{}),然后使用類(lèi)型斷言根據(jù)具體的類(lèi)型來(lái)執(zhí)行不同的轉(zhuǎn)換。如果無(wú)法轉(zhuǎn)換,返回錯(cuò)誤。
// ToBoolE casts an interface to a bool type.
func ToBoolE(i interface{}) (bool, error) {
i = indirect(i)
switch b := i.(type) {
case bool:
return b, nil
case nil:
return false, nil
case int:
if i.(int) != 0 {
return true, nil
}
return false, nil
case string:
return strconv.ParseBool(i.(string))
case json.Number:
v, err := ToInt64E(b)
if err == nil {
return v != 0, nil
}
return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i)
default:
return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i)
}
}
首先調(diào)用indirect函數(shù)將參數(shù)中可能的指針去掉。如果類(lèi)型本身不是指針,那么直接返回。否則返回指針指向的值。循環(huán)直到返回一個(gè)非指針的值:
// From html/template/content.go
// Copyright 2011 The Go Authors. All rights reserved.
// indirect returns the value, after dereferencing as many times
// as necessary to reach the base type (or nil).
func indirect(a interface{}) interface{} {
if a == nil {
return nil
}
if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
// Avoid creating a reflect.Value if it's not a pointer.
return a
}
v := reflect.ValueOf(a)
for v.Kind() == reflect.Ptr && !v.IsNil() {
v = v.Elem()
}
return v.Interface()
}
所以,下面代碼輸出都是 8:
package main
import (
"fmt"
"github.com/spf13/cast"
)
func main() {
p := new(int)
*p = 8
fmt.Println(cast.ToInt(p)) // 8
pp := &p
fmt.Println(cast.ToInt(pp)) // 8
}
時(shí)間和時(shí)長(zhǎng)轉(zhuǎn)換
時(shí)間類(lèi)型的轉(zhuǎn)換
源碼如下:
// ToTimeE casts an interface to a time.Time type.
func ToTimeE(i interface{}) (tim time.Time, err error) {
return ToTimeInDefaultLocationE(i, time.UTC)
}
// ToTimeInDefaultLocationE casts an empty interface to time.Time,
// interpreting inputs without a timezone to be in the given location,
// or the local timezone if nil.
func ToTimeInDefaultLocationE(i interface{}, location *time.Location) (tim time.Time, err error) {
i = indirect(i)
switch v := i.(type) {
case time.Time:
return v, nil
case string:
return StringToDateInDefaultLocation(v, location)
case json.Number:
s, err1 := ToInt64E(v)
if err1 != nil {
return time.Time{}, fmt.Errorf("unable to cast %#v of type %T to Time", i, i)
}
return time.Unix(s, 0), nil
case int:
return time.Unix(int64(v), 0), nil
case int64:
return time.Unix(v, 0), nil
case int32:
return time.Unix(int64(v), 0), nil
case uint:
return time.Unix(int64(v), 0), nil
case uint64:
return time.Unix(int64(v), 0), nil
case uint32:
return time.Unix(int64(v), 0), nil
default:
return time.Time{}, fmt.Errorf("unable to cast %#v of type %T to Time", i, i)
}
}
根據(jù)傳入的類(lèi)型執(zhí)行不同的處理:
- 如果是
time.Time,直接返回; - 如果是整型,將參數(shù)作為時(shí)間戳(自
UTC時(shí)間1970.01.01 00:00:00到現(xiàn)在的秒數(shù))調(diào)用time.Unix生成時(shí)間。Unix接受兩個(gè)參數(shù),第一個(gè)參數(shù)指定秒,第二個(gè)參數(shù)指定納秒; - 如果是字符串,調(diào)用
StringToDate函數(shù)依次嘗試以下面這些時(shí)間格式調(diào)用time.Parse解析該字符串。如果某個(gè)格式解析成功,則返回獲得的time.Time。否則解析失敗,返回錯(cuò)誤; - 其他任何類(lèi)型都無(wú)法轉(zhuǎn)換為
time.Time。
字符串轉(zhuǎn)換為時(shí)間
源碼:
// cast/caste.go
func StringToDate(s string) (time.Time, error) {
return parseDateWith(s, []string{
time.RFC3339,
"2006-01-02T15:04:05", // iso8601 without timezone
time.RFC1123Z,
time.RFC1123,
time.RFC822Z,
time.RFC822,
time.RFC850,
time.ANSIC,
time.UnixDate,
time.RubyDate,
"2006-01-02 15:04:05.999999999 -0700 MST", // Time.String()
"2006-01-02",
"02 Jan 2006",
"2006-01-02T15:04:05-0700", // RFC3339 without timezone hh:mm colon
"2006-01-02 15:04:05 -07:00",
"2006-01-02 15:04:05 -0700",
"2006-01-02 15:04:05Z07:00", // RFC3339 without T
"2006-01-02 15:04:05Z0700", // RFC3339 without T or timezone hh:mm colon
"2006-01-02 15:04:05",
time.Kitchen,
time.Stamp,
time.StampMilli,
time.StampMicro,
time.StampNano,
})
}
func parseDateWith(s string, dates []string) (d time.Time, e error) {
for _, dateType := range dates {
if d, e = time.Parse(dateType, s); e == nil {
return
}
}
return d, fmt.Errorf("unable to parse date: %s", s)
}
時(shí)長(zhǎng)類(lèi)型的轉(zhuǎn)換
源碼如下:
// cast/caste.go
func ToDurationE(i interface{}) (d time.Duration, err error) {
i = indirect(i)
switch s := i.(type) {
case time.Duration:
return s, nil
case int, int64, int32, int16, int8, uint, uint64, uint32, uint16, uint8:
d = time.Duration(ToInt64(s))
return
case float32, float64:
d = time.Duration(ToFloat64(s))
return
case string:
if strings.ContainsAny(s, "nsuμmh") {
d, err = time.ParseDuration(s)
} else {
d, err = time.ParseDuration(s "ns")
}
return
default:
err = fmt.Errorf("unable to cast %#v of type %T to Duration", i, i)
return
}
}
根據(jù)傳入的類(lèi)型進(jìn)行不同的處理:
- 如果是
time.Duration類(lèi)型,直接返回; - 如果是整型或浮點(diǎn)型,將其數(shù)值強(qiáng)制轉(zhuǎn)換為
time.Duration類(lèi)型,單位默認(rèn)為ns; - 如果是字符串,分為兩種情況:如果字符串中有時(shí)間單位符號(hào)
nsuµmh,直接調(diào)用time.ParseDuration解析;否則在字符串后拼接ns再調(diào)用time.ParseDuration解析; - 其他類(lèi)型解析失敗。
示例:
package main
import (
"fmt"
"time"
"github.com/spf13/cast"
)
func main() {
now := time.Now()
timestamp := 1579615973
timeStr := "2020-01-21 22:13:48"
fmt.Println(cast.ToTime(now)) // 2020-01-22 06:31:50.5068465 0800 CST m= 0.000997701
fmt.Println(cast.ToTime(timestamp)) // 2020-01-21 22:12:53 0800 CST
fmt.Println(cast.ToTime(timeStr)) // 2020-01-21 22:13:48 0000 UTC
d, _ := time.ParseDuration("1m30s")
ns := 30000
strWithUnit := "130s"
strWithoutUnit := "130"
fmt.Println(cast.ToDuration(d)) // 1m30s
fmt.Println(cast.ToDuration(ns)) // 30μs
fmt.Println(cast.ToDuration(strWithUnit)) // 2m10s
fmt.Println(cast.ToDuration(strWithoutUnit)) // 130ns
}
轉(zhuǎn)換為切片
實(shí)際上,這些函數(shù)的實(shí)現(xiàn)基本類(lèi)似。使用類(lèi)型斷言判斷類(lèi)型。如果就是要返回的類(lèi)型,直接返回。否則根據(jù)類(lèi)型進(jìn)行相應(yīng)的轉(zhuǎn)換。
我們主要分析兩個(gè)實(shí)現(xiàn):ToIntSliceE和ToStringSliceE。ToBoolSliceE/ToDurationSliceE與ToIntSliceE基本相同。
ToIntSliceE
源碼如下:
func ToIntSliceE(i interface{}) ([]int, error) {
if i == nil {
return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i)
}
switch v := i.(type) {
case []int:
return v, nil
}
kind := reflect.TypeOf(i).Kind()
switch kind {
case reflect.Slice, reflect.Array:
s := reflect.ValueOf(i)
a := make([]int, s.Len())
for j := 0; j < s.Len(); j {
val, err := ToIntE(s.Index(j).Interface())
if err != nil {
return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i)
}
a[j] = val
}
return a, nil
default:
return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i)
}
}
根據(jù)傳入?yún)?shù)的類(lèi)型:
- 如果是
nil,直接返回錯(cuò)誤; - 如果是
[]int,不用轉(zhuǎn)換,直接返回; - 如果傳入類(lèi)型為切片或數(shù)組,新建一個(gè)
[]int,將切片或數(shù)組中的每個(gè)元素轉(zhuǎn)為int放到該[]int中。最后返回這個(gè)[]int; - 其他情況,不能轉(zhuǎn)換。
ToStringSliceE
源碼如下:
func ToStringSliceE(i interface{}) ([]string, error) {
var a []string
switch v := i.(type) {
case []interface{}:
for _, u := range v {
a = append(a, ToString(u))
}
return a, nil
case []string:
return v, nil
case string:
return strings.Fields(v), nil
case interface{}:
str, err := ToStringE(v)
if err != nil {
return a, fmt.Errorf("unable to cast %#v of type %T to []string", i, i)
}
return []string{str}, nil
default:
return a, fmt.Errorf("unable to cast %#v of type %T to []string", i, i)
}
}
根據(jù)傳入的參數(shù)類(lèi)型:
- 如果是
[]interface{},將該參數(shù)中每個(gè)元素轉(zhuǎn)為string,返回結(jié)果切片; - 如果是
[]string,不需要轉(zhuǎn)換,直接返回; - 如果是
interface{},將參數(shù)轉(zhuǎn)為string,返回只包含這個(gè)值的切片; - 如果是
string,調(diào)用strings.Fields函數(shù)按空白符將參數(shù)拆分,返回拆分后的字符串切片; - 其他情況,不能轉(zhuǎn)換。
示例:
package main
import (
"fmt"
"github.com/spf13/cast"
)
func main() {
sliceOfInt := []int{1, 3, 7}
arrayOfInt := [3]int{8, 12}
// ToIntSlice
fmt.Println(cast.ToIntSlice(sliceOfInt)) // [1 3 7]
fmt.Println(cast.ToIntSlice(arrayOfInt)) // [8 12 0]
sliceOfInterface := []interface{}{1, 2.0, "darjun"}
sliceOfString := []string{"abc", "dj", "pipi"}
stringFields := " abc def hij "
any := interface{}(37)
// ToStringSliceE
fmt.Println(cast.ToStringSlice(sliceOfInterface)) // [1 2 darjun]
fmt.Println(cast.ToStringSlice(sliceOfString)) // [abc dj pipi]
fmt.Println(cast.ToStringSlice(stringFields)) // [abc def hij]
fmt.Println(cast.ToStringSlice(any)) // [37]
}
轉(zhuǎn)為map[string]Type類(lèi)型
cast庫(kù)能將傳入的參數(shù)轉(zhuǎn)為map[string]Type類(lèi)型,Type為上面支持的類(lèi)型。
其實(shí)只需要分析一個(gè)ToStringMapStringE函數(shù)就可以了,其他的實(shí)現(xiàn)基本一樣。ToStringMapStringE返回map[string]string類(lèi)型的值。
源碼如下:
func ToStringMapStringE(i interface{}) (map[string]string, error) {
var m = map[string]string{}
switch v := i.(type) {
case map[string]string:
return v, nil
case map[string]interface{}:
for k, val := range v {
m[ToString(k)] = ToString(val)
}
return m, nil
case map[interface{}]string:
for k, val := range v {
m[ToString(k)] = ToString(val)
}
return m, nil
case map[interface{}]interface{}:
for k, val := range v {
m[ToString(k)] = ToString(val)
}
return m, nil
case string:
err := jsonStringToObject(v, &m)
return m, err
default:
return m, fmt.Errorf("unable to cast %#v of type %T to map[string]string", i, i)
}
}
根據(jù)傳入的參數(shù)類(lèi)型:
- 如果是
map[string]string,不用轉(zhuǎn)換,直接返回; - 如果是
map[string]interface{},將每個(gè)值轉(zhuǎn)為string存入新的 map,最后返回新的 map; - 如果是
map[interface{}]string,將每個(gè)鍵轉(zhuǎn)為string存入新的 map,最后返回新的 map; - 如果是
map[interface{}]interface{},將每個(gè)鍵和值都轉(zhuǎn)為string存入新的 map,最后返回新的 map; - 如果是
string類(lèi)型,cast將它看成一個(gè) JSON 串,解析這個(gè) JSON 到map[string]string,然后返回結(jié)果; - 其他情況,返回錯(cuò)誤。
示例:
package main
import (
"fmt"
"github.com/spf13/cast"
)
func main() {
m1 := map[string]string {
"name": "darjun",
"job": "developer",
}
m2 := map[string]interface{} {
"name": "jingwen",
"age": 18,
}
m3 := map[interface{}]string {
"name": "pipi",
"job": "designer",
}
m4 := map[interface{}]interface{} {
"name": "did",
"age": 29,
}
jsonStr := `{"name":"bibi", "job":"manager"}`
fmt.Println(cast.ToStringMapString(m1)) // map[job:developer name:darjun]
fmt.Println(cast.ToStringMapString(m2)) // map[age:18 name:jingwen]
fmt.Println(cast.ToStringMapString(m3)) // map[job:designer name:pipi]
fmt.Println(cast.ToStringMapString(m4)) // map[job:designer name:pipi]
fmt.Println(cast.ToStringMapString(jsonStr)) // map[job:manager name:bibi]
}
到此這篇關(guān)于Go語(yǔ)言類(lèi)型轉(zhuǎn)換工具庫(kù)cast的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go語(yǔ)言類(lèi)型轉(zhuǎn)換工具庫(kù)cast內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang實(shí)現(xiàn)多協(xié)程下載文件(支持?jǐn)帱c(diǎn)續(xù)傳)
本文主要介紹了golang實(shí)現(xiàn)多協(xié)程下載文件,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
深入探究Golang中l(wèi)og標(biāo)準(zhǔn)庫(kù)的使用
Go?語(yǔ)言標(biāo)準(zhǔn)庫(kù)中的?log?包設(shè)計(jì)簡(jiǎn)潔明了,易于上手,可以輕松記錄程序運(yùn)行時(shí)的信息、調(diào)試錯(cuò)誤以及跟蹤代碼執(zhí)行過(guò)程中的問(wèn)題等。本文主要來(lái)深入探究?log?包的使用和原理,幫助讀者更好地了解和掌握它2023-05-05
2026年Go語(yǔ)言開(kāi)發(fā)中沒(méi)有它就不行的10個(gè)庫(kù)總結(jié)
Go語(yǔ)言以其簡(jiǎn)潔高效、并發(fā)安全、跨平臺(tái)編譯等特性,在后端服務(wù)、云原生、DevOps 工具等領(lǐng)域占據(jù)重要地位,這篇文章主要介紹了2026年Go語(yǔ)言開(kāi)發(fā)中沒(méi)有它就不行的10個(gè)庫(kù),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2026-05-05
go mod tidy拉取依賴(lài)包bug問(wèn)題及解決
這篇文章主要介紹了go mod tidy拉取依賴(lài)包bug問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09
使用Go module和GoLand初始化一個(gè)Go項(xiàng)目的方法
這篇文章主要介紹了使用Go module和GoLand初始化一個(gè)Go項(xiàng)目,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12

