4 Star 6 Fork 3

王军 / golib

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
decode.go 7.32 KB
一键复制 编辑 原始数据 按行查看 历史
王军 提交于 2022-10-21 18:04 . 修复多一行的问题
// Package csvstruct provides methods to decode a CSV file into a struct.
package csvstruct
import (
"encoding"
"encoding/csv"
"errors"
"fmt"
"io"
"reflect"
"strconv"
"strings"
"golang.org/x/text/encoding/simplifiedchinese"
)
var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
// Decoder reads and decodes CSV rows from an input stream.
type Decoder interface {
// DecodeNext populates v with the values from the next row in the
// Decoder's Reader.
//
// On the first call to DecodeNext, the first row in the reader will be
// used as the header row to map CSV fields to struct fields, and the
// second row will be read to populate v.
DecodeNext(v interface{}) error
//DecodeNextValue(v reflect.Value) error
// Opts specifies options to modify decoding behavior.
//
// It returns the Decoder, to support chaining.
Opts(DecodeOpts) Decoder
}
// DecodeOpts specifies options to modify decoding behavior.
type DecodeOpts struct {
Comma rune // field delimiter (set to ',' by default)
Comment rune // comment character for start of line
LazyQuotes bool // allow lazy quotes
TrimLeadingSpace bool // trim leading space
}
type decoder struct {
r csv.Reader
hm map[string]int
}
/**
* @description: ansi 的编码识别
* @param {io.Reader} r
* @return {*}
*/
func NewDocedeWithAnsi(r io.Reader) Decoder {
dec := simplifiedchinese.GBK.NewDecoder()
return NewDecoder(dec.Reader(r))
}
// NewDecoder returns a Decoder that reads from r.
func NewDecoder(r io.Reader) Decoder {
csvr := csv.NewReader(r)
return &decoder{r: *csvr}
}
/**
* @description:
* @param {[]byte} data
* @param {any} v 数组地址
* @return {*}
*/
func Decode(r io.Reader, slice any) (err error) {
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Pointer || v.IsNil() {
return &InvalidUnmarshalError{reflect.TypeOf(slice)}
}
v = v.Elem()
if v.Kind() != reflect.Slice {
return errors.New("参数必须是 slice")
}
dec := NewDecoder(r)
idx := 0
lineNo := 1
for {
// Grow slice if necessary
if idx >= v.Cap() {
newcap := v.Cap() + v.Cap()/2
if newcap < 4 {
newcap = 4
}
newv := reflect.MakeSlice(v.Type(), v.Len(), newcap)
reflect.Copy(newv, v)
v.Set(newv)
v.SetLen(newcap)
}
lineNo++
vv := v.Index(idx)
if vv.Kind() == reflect.Ptr {
if vv.IsNil() {
vv.Set(reflect.New(vv.Type().Elem()))
}
}
err = dec.DecodeNext(vv)
if err == io.EOF {
err = nil
break
}
if err != nil {
return
}
idx++
}
if idx < v.Len() {
v.SetLen(idx)
}
return
}
// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal.
// (The argument to Unmarshal must be a non-nil pointer.)
type InvalidUnmarshalError struct {
Type reflect.Type
}
func (e *InvalidUnmarshalError) Error() string {
if e.Type == nil {
return "csv: Unmarshal(nil)"
}
if e.Type.Kind() != reflect.Pointer {
return "csv: Unmarshal(non-pointer " + e.Type.String() + ")"
}
return "csv: Unmarshal(nil " + e.Type.String() + ")"
}
/**
* @description: 解析ansi格式的csv
* @param {io.Reader} r
* @param {any} v 数组地址
* @return {*}
*/
func DecodeWithAnsi(r io.Reader, v any) (err error) {
dec := simplifiedchinese.GBK.NewDecoder()
return Decode(dec.Reader(r), v)
}
func (d *decoder) Opts(opts DecodeOpts) Decoder {
if opts.Comma != rune(0) {
d.r.Comma = opts.Comma
}
if opts.Comment != rune(0) {
d.r.Comment = opts.Comment
}
d.r.LazyQuotes = opts.LazyQuotes
d.r.TrimLeadingSpace = opts.TrimLeadingSpace
return d
}
func (d *decoder) DecodeNext(v interface{}) error {
line, err := d.read()
if err != nil {
return err
}
// v is nil, skip this line and proceed.
if v == nil {
return nil
}
rv, ok := v.(reflect.Value)
if !ok {
rv = reflect.ValueOf(v)
}
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
} else if rv.Kind() != reflect.Map {
return errors.New("must be pointer")
}
switch rv.Type().Kind() {
case reflect.Map:
return d.decodeMap(rv, line)
case reflect.Struct:
return d.decodeStruct(rv, line)
default:
return errors.New("must be pointer to struct")
}
}
func (d *decoder) decodeMap(v reflect.Value, line []string) error {
t := v.Type()
if t.Key().Kind() != reflect.String {
return errors.New("map key must be string")
}
switch t.Elem().Kind() {
case reflect.String:
m := v.Interface().(map[string]string)
for hv, hidx := range d.hm {
m[hv] = line[hidx]
}
// TODO: Support arbitrary map values by parsing string values
case reflect.Interface:
return errors.New("TODO")
default:
return fmt.Errorf("can't decode type %v", t.Elem().Kind())
}
return nil
}
func (d *decoder) decodeStruct(rv reflect.Value, line []string) error {
// rv, ok := v.(reflect.Value)
// if !ok {
// rv = reflect.ValueOf(v)
// }
//rv = rv.Elem()
t := rv.Type()
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Anonymous {
if f.Type.Kind() == reflect.Struct {
v := rv.Field(i)
if v.CanAddr() {
//item := v.Addr()
d.decodeStruct(v, line)
}
}
continue
}
if !(f.Name[0] >= 'A' && f.Name[0] <= 'Z') {
continue
}
n := f.Name
omitempty := false
if tag := f.Tag.Get("csv"); tag != "" {
parts := strings.Split(tag, ",")
if tagn := parts[0]; tag == "-" {
continue
} else if tagn != "" {
n = tagn
}
omitempty = len(parts) > 1 && parts[1] == "omitempty"
}
n = strings.ToLower(n)
idx, ok := d.hm[n]
if !ok {
// Unmapped header value
continue
}
strv := line[idx]
vf := rv.FieldByName(f.Name)
if vf.CanSet() {
if vf.CanInterface() && vf.Type().Implements(textUnmarshalerType) {
if vf.IsNil() {
vf.Set(reflect.New(vf.Type().Elem()))
}
if tu, ok := vf.Interface().(encoding.TextUnmarshaler); ok {
if err := tu.UnmarshalText([]byte(strv)); err != nil {
return err
}
continue
} else {
panic("unreachable")
}
}
if vf.Kind() == reflect.Ptr {
if omitempty && strv == "" {
continue
}
if vf.IsNil() {
vf.Set(reflect.New(vf.Type().Elem()))
}
vf = vf.Elem()
}
switch vf.Kind() {
case reflect.String:
vf.SetString(strv)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
i, err := strconv.ParseInt(strv, 10, 64)
if err != nil {
return fmt.Errorf("error decoding: %v", err)
}
vf.SetInt(i)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
u, err := strconv.ParseUint(strv, 10, 64)
if err != nil {
return fmt.Errorf("error decoding: %v", err)
}
vf.SetUint(u)
case reflect.Float64:
f, err := strconv.ParseFloat(strv, 64)
if err != nil {
return fmt.Errorf("error decoding: %v", err)
}
vf.SetFloat(f)
case reflect.Bool:
b, err := strconv.ParseBool(strv)
if err != nil {
return fmt.Errorf("error decoding: %v", err)
}
vf.SetBool(b)
default:
return fmt.Errorf("can't decode type %v", vf.Type())
}
}
}
return nil
}
func (d *decoder) read() ([]string, error) {
if d.hm == nil {
// First run; read header row
header, err := d.r.Read()
if err != nil {
return nil, fmt.Errorf("error reading headers: %v", err)
}
d.hm = reverse(header)
}
// Read data row into []string
return d.r.Read()
}
func reverse(in []string) map[string]int {
m := make(map[string]int, len(in))
for i, v := range in {
v = strings.ToLower(v)
m[v] = i
}
return m
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/haodreams/golib.git
git@gitee.com:haodreams/golib.git
haodreams
golib
golib
f73911ac4441

搜索帮助

344bd9b3 5694891 D2dac590 5694891