• golang 任意类型之间相互转换


    在处理一些参数的时候,可能需要将参数转换为各种类型,这里实现一个通用的转换函数,实现各种类型之间的相互转换。

    当然,如果源数据格式和目标数据类型不一致,是会返回错误的。例如将字符串“一二三”转换为数值类型则会报错,而将字符串“123”转换为数值类型则OK。

    这段代码实际抄自go自带的“database/sql”库,只是源代码作为内部函数无法在外面调用,可以单独把需要的功能拎出来使用:

    代码中有一个Scaner接口,可以自行实现,然后通过"convertAssign()"函数,作为dst参数传入。

    package main
    
    import (
    	"errors"
    	"fmt"
    	"reflect"
    	"strconv"
    	"time"
    )
    
    // RawBytes is a byte slice that holds a reference to memory owned by
    // the database itself. After a Scan into a RawBytes, the slice is only
    // valid until the next call to Next, Scan, or Close.
    type RawBytes []byte
    
    var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error
    
    // convertAssign copies to dest the value in src, converting it if possible.
    // An error is returned if the copy would result in loss of information.
    // dest should be a pointer type.
    func convertAssign(dest, src interface{}) error {
    	// Common cases, without reflect.
    	switch s := src.(type) {
    	case string:
    		switch d := dest.(type) {
    		case *string:
    			if d == nil {
    				return errNilPtr
    			}
    			*d = s
    			return nil
    		case *[]byte:
    			if d == nil {
    				return errNilPtr
    			}
    			*d = []byte(s)
    			return nil
    		case *RawBytes:
    			if d == nil {
    				return errNilPtr
    			}
    			*d = append((*d)[:0], s...)
    			return nil
    		}
    	case []byte:
    		switch d := dest.(type) {
    		case *string:
    			if d == nil {
    				return errNilPtr
    			}
    			*d = string(s)
    			return nil
    		case *interface{}:
    			if d == nil {
    				return errNilPtr
    			}
    			*d = cloneBytes(s)
    			return nil
    		case *[]byte:
    			if d == nil {
    				return errNilPtr
    			}
    			*d = cloneBytes(s)
    			return nil
    		case *RawBytes:
    			if d == nil {
    				return errNilPtr
    			}
    			*d = s
    			return nil
    		}
    	case time.Time:
    		switch d := dest.(type) {
    		case *time.Time:
    			*d = s
    			return nil
    		case *string:
    			*d = s.Format(time.RFC3339Nano)
    			return nil
    		case *[]byte:
    			if d == nil {
    				return errNilPtr
    			}
    			*d = []byte(s.Format(time.RFC3339Nano))
    			return nil
    		case *RawBytes:
    			if d == nil {
    				return errNilPtr
    			}
    			*d = s.AppendFormat((*d)[:0], time.RFC3339Nano)
    			return nil
    		}
    	case nil:
    		switch d := dest.(type) {
    		case *interface{}:
    			if d == nil {
    				return errNilPtr
    			}
    			*d = nil
    			return nil
    		case *[]byte:
    			if d == nil {
    				return errNilPtr
    			}
    			*d = nil
    			return nil
    		case *RawBytes:
    			if d == nil {
    				return errNilPtr
    			}
    			*d = nil
    			return nil
    		}
    	}
    
    	var sv reflect.Value
    
    	switch d := dest.(type) {
    	case *string:
    		sv = reflect.ValueOf(src)
    		switch sv.Kind() {
    		case reflect.Bool,
    			reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
    			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
    			reflect.Float32, reflect.Float64:
    			*d = asString(src)
    			return nil
    		}
    	case *[]byte:
    		sv = reflect.ValueOf(src)
    		if b, ok := asBytes(nil, sv); ok {
    			*d = b
    			return nil
    		}
    	case *RawBytes:
    		sv = reflect.ValueOf(src)
    		if b, ok := asBytes([]byte(*d)[:0], sv); ok {
    			*d = RawBytes(b)
    			return nil
    		}
    	case *bool:
    		bv, err := Bool.ConvertValue(src)
    		if err == nil {
    			*d = bv.(bool)
    		}
    		return err
    	case *interface{}:
    		*d = src
    		return nil
    	}
    
    	if scanner, ok := dest.(Scanner); ok {
    		return scanner.Scan(src)
    	}
    
    	dpv := reflect.ValueOf(dest)
    	if dpv.Kind() != reflect.Ptr {
    		return errors.New("destination not a pointer")
    	}
    	if dpv.IsNil() {
    		return errNilPtr
    	}
    
    	if !sv.IsValid() {
    		sv = reflect.ValueOf(src)
    	}
    
    	dv := reflect.Indirect(dpv)
    	if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) {
    		switch b := src.(type) {
    		case []byte:
    			dv.Set(reflect.ValueOf(cloneBytes(b)))
    		default:
    			dv.Set(sv)
    		}
    		return nil
    	}
    
    	if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) {
    		dv.Set(sv.Convert(dv.Type()))
    		return nil
    	}
    
    	// The following conversions use a string value as an intermediate representation
    	// to convert between various numeric types.
    	//
    	// This also allows scanning into user defined types such as "type Int int64".
    	// For symmetry, also check for string destination types.
    	switch dv.Kind() {
    	case reflect.Ptr:
    		if src == nil {
    			dv.Set(reflect.Zero(dv.Type()))
    			return nil
    		}
    		dv.Set(reflect.New(dv.Type().Elem()))
    		return convertAssign(dv.Interface(), src)
    	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    		s := asString(src)
    		i64, err := strconv.ParseInt(s, 10, dv.Type().Bits())
    		if err != nil {
    			err = strconvErr(err)
    			return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
    		}
    		dv.SetInt(i64)
    		return nil
    	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    		s := asString(src)
    		u64, err := strconv.ParseUint(s, 10, dv.Type().Bits())
    		if err != nil {
    			err = strconvErr(err)
    			return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
    		}
    		dv.SetUint(u64)
    		return nil
    	case reflect.Float32, reflect.Float64:
    		s := asString(src)
    		f64, err := strconv.ParseFloat(s, dv.Type().Bits())
    		if err != nil {
    			err = strconvErr(err)
    			return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
    		}
    		dv.SetFloat(f64)
    		return nil
    	case reflect.String:
    		switch v := src.(type) {
    		case string:
    			dv.SetString(v)
    			return nil
    		case []byte:
    			dv.SetString(string(v))
    			return nil
    		}
    	}
    
    	return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest)
    }
    
    func strconvErr(err error) error {
    	if ne, ok := err.(*strconv.NumError); ok {
    		return ne.Err
    	}
    	return err
    }
    
    func cloneBytes(b []byte) []byte {
    	if b == nil {
    		return nil
    	}
    	c := make([]byte, len(b))
    	copy(c, b)
    	return c
    }
    
    func asString(src interface{}) string {
    	switch v := src.(type) {
    	case string:
    		return v
    	case []byte:
    		return string(v)
    	}
    	rv := reflect.ValueOf(src)
    	switch rv.Kind() {
    	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    		return strconv.FormatInt(rv.Int(), 10)
    	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    		return strconv.FormatUint(rv.Uint(), 10)
    	case reflect.Float64:
    		return strconv.FormatFloat(rv.Float(), 'g', -1, 64)
    	case reflect.Float32:
    		return strconv.FormatFloat(rv.Float(), 'g', -1, 32)
    	case reflect.Bool:
    		return strconv.FormatBool(rv.Bool())
    	}
    	return fmt.Sprintf("%v", src)
    }
    
    func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) {
    	switch rv.Kind() {
    	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    		return strconv.AppendInt(buf, rv.Int(), 10), true
    	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    		return strconv.AppendUint(buf, rv.Uint(), 10), true
    	case reflect.Float32:
    		return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true
    	case reflect.Float64:
    		return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true
    	case reflect.Bool:
    		return strconv.AppendBool(buf, rv.Bool()), true
    	case reflect.String:
    		s := rv.String()
    		return append(buf, s...), true
    	}
    	return
    }
    
    // Value is a value that drivers must be able to handle.
    // It is either nil, a type handled by a database driver's NamedValueChecker
    // interface, or an instance of one of these types:
    //
    //   int64
    //   float64
    //   bool
    //   []byte
    //   string
    //   time.Time
    type Value interface{}
    
    type boolType struct{}
    var Bool boolType
    func (boolType) String() string { return "Bool" }
    func (boolType) ConvertValue(src interface{}) (Value, error) {
    	switch s := src.(type) {
    	case bool:
    		return s, nil
    	case string:
    		b, err := strconv.ParseBool(s)
    		if err != nil {
    			return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s)
    		}
    		return b, nil
    	case []byte:
    		b, err := strconv.ParseBool(string(s))
    		if err != nil {
    			return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s)
    		}
    		return b, nil
    	}
    
    	sv := reflect.ValueOf(src)
    	switch sv.Kind() {
    	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    		iv := sv.Int()
    		if iv == 1 || iv == 0 {
    			return iv == 1, nil
    		}
    		return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", iv)
    	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    		uv := sv.Uint()
    		if uv == 1 || uv == 0 {
    			return uv == 1, nil
    		}
    		return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", uv)
    	}
    
    	return nil, fmt.Errorf("sql/driver: couldn't convert %v (%T) into type bool", src, src)
    }
    
    type Scanner interface {
    	// Scan assigns a value from a database driver.
    	//
    	// The src value will be of one of the following types:
    	//
    	//    int64
    	//    float64
    	//    bool
    	//    []byte
    	//    string
    	//    time.Time
    	//    nil - for NULL values
    	//
    	// An error should be returned if the value cannot be stored
    	// without loss of information.
    	//
    	// Reference types such as []byte are only valid until the next call to Scan
    	// and should not be retained. Their underlying memory is owned by the driver.
    	// If retention is necessary, copy their values before the next call to Scan.
    	Scan(src interface{}) error
    }
    

      

    使用方法:

    package main
    
    import "log"
    
    func main()  {
    
    	var dst1 float64
    	var dst2 int
    
    	src := "123"
    
    	if err := convertAssign(&dst1, src); err != nil {
    		log.Fatalf("convert failed, %v", err)
    	}else {
    		log.Printf("convert ok: %f", dst1)
    	}
    
    	if err := convertAssign(&dst2, src); err != nil {
    		log.Fatalf("convert failed, %v", err)
    	}else {
    		log.Printf("convert ok: %d", dst2)
    	}
    }
    

      

    例子输出结果如下:

    2019/02/15 16:59:39 convert ok: 123.000000
    2019/02/15 16:59:39 convert ok: 123

    怎么样,是不是很强大

  • 相关阅读:
    高阶篇:1.2)材料和工艺的选择
    高阶篇:4.1.1)QFDI(客户需求转换为设计要求)
    高阶篇:4)可靠性设计-总章
    高阶篇:1.5)如何选择更好的概念-Pugh矩阵法
    知识点篇:2)产品结构设计目标的分类
    高阶篇:1.1)竞品(标杆产品)的拆解和分析benchmarking
    支持向量机
    机器学习概述
    HDU_oj_2055 An easy problem
    HDU_oj_2054 A==B ?
  • 原文地址:https://www.cnblogs.com/yzhch/p/10384603.html
Copyright © 2020-2023  润新知