在Go中将CSV记录解组到结构中

guykilcj  于 2023-04-18  发布在  Go
关注(0)|答案(5)|浏览(186)

如何将CSV文件中的记录自动反序列化/解组到Go结构中的问题。
例如,我有

type Test struct {
  Name string
  Surname string
  Age int
}

CSV文件包含记录

John;Smith;42
Piter;Abel;50

除了使用“encoding/csv”包阅读记录,然后执行类似以下操作之外,是否有一种简单的方法将这些记录解组为结构体

record, _ := reader.Read()
test := Test{record[0],record[1],atoi(record[2])}
xienkqul

xienkqul1#

gocarina/gocsv可以像encoding/json一样处理自定义结构体。你也可以为特定类型编写自定义的marshaller和解组器。
示例:

type Client struct {
    Id      string `csv:"client_id"` // .csv column headers
    Name    string `csv:"client_name"`
    Age     string `csv:"client_age"`
}

func main() {
    in, err := os.Open("clients.csv")
    if err != nil {
        panic(err)
    }
    defer in.Close()

    clients := []*Client{}

    if err := gocsv.UnmarshalFile(in, &clients); err != nil {
        panic(err)
    }
    for _, client := range clients {
        fmt.Println("Hello, ", client.Name)
    }
}
7bsow1i6

7bsow1i62#

似乎我已经完成了CSV记录到结构体的自动封送(仅限于字符串和整型)。希望这会有用。
这里是一个链接到操场:http://play.golang.org/p/kwc32A5mJf

func Unmarshal(reader *csv.Reader, v interface{}) error {
    record, err := reader.Read()
    if err != nil {
        return err
    }
    s := reflect.ValueOf(v).Elem()
    if s.NumField() != len(record) {
        return &FieldMismatch{s.NumField(), len(record)}
    }
    for i := 0; i < s.NumField(); i++ {
        f := s.Field(i)
        switch f.Type().String() {
        case "string":
            f.SetString(record[i])
        case "int":
            ival, err := strconv.ParseInt(record[i], 10, 0)
            if err != nil {
                return err
            }
            f.SetInt(ival)
        default:
            return &UnsupportedType{f.Type().String()}
        }
    }
    return nil
}

我将尝试创建github包是有人需要这个实现。

a8jjtwal

a8jjtwal3#

你可以自己烤一个。也许像这样:

package main

import (
    "fmt"
    "strconv"
    "strings"
)

type Test struct {
    Name    string
    Surname string
    Age     int
}

func (t Test) String() string {
    return fmt.Sprintf("%s;%s;%d", t.Name, t.Surname, t.Age)
}

func (t *Test) Parse(in string) {
    tmp := strings.Split(in, ";")
    t.Name = tmp[0]
    t.Surname = tmp[1]
    t.Age, _ = strconv.Atoi(tmp[2])
}

func main() {

    john := Test{"John", "Smith", 42}
    fmt.Printf("john:%v\n", john)

    johnString := john.String()
    fmt.Printf("johnString:%s\n", johnString)

    var rebornJohn Test
    rebornJohn.Parse(johnString)
    fmt.Printf("rebornJohn:%v\n", rebornJohn)

}
vfhzx4xs

vfhzx4xs4#

使用csvutil可以给予列标题,参见示例。
在您的情况下,这可能是:

package main

import (
    "encoding/csv"
    "fmt"
    "io"
    "os"

    "github.com/jszwec/csvutil"
)

type Test struct {
    Name    string
    Surname string
    Age     int
}

func main() {
    csv_file, _ := os.Open("test.csv")
    reader := csv.NewReader(csv_file)
    reader.Comma = ';'

    userHeader, _ := csvutil.Header(Test{}, "csv")
    dec, _ := csvutil.NewDecoder(reader, userHeader...)

    var users []Test
    for {
        var u Test
        if err := dec.Decode(&u); err == io.EOF {
            break
        }
        users = append(users, u)
    }

    fmt.Println(users)
}
bhmjp9jg

bhmjp9jg5#

解决这个问题的一个简单方法是使用JSON作为中间表示。
一旦你做到了这一点,你有各种各样的工具在你的处置。
你可以…

  • 直接解封为您的类型(如果全部为字符串)
  • 解封送到map[string]interface{},然后进行任何必要的类型转换
  • 反编组-〉转换类型-〉重新编组JSON -〉反编组为您的类型

下面是一个简单的通用编组函数,它启用了这个流。。

pairToJSON := func(header, record []string) string {
    raw := ""
    for j, v := range record {
        if j != 0 {
            raw += ",\n"
        }
        raw += "\"" + header[j] + "\":\"" + v + "\""
    }
    raw = "{\n" + raw + "\n}"
    return raw
}

以上与the standard csv library产生的[]string数据兼容。

相关问题