英文:
Unmarshal CSV record into struct in Go
问题
问题是如何将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])}
英文:
The problem how to automatically deserialize/unmarshal record from CSV file into Go struct.
For example, I have
type Test struct {
Name string
Surname string
Age int
}
And CSV file contains records
John;Smith;42
Piter;Abel;50
Is there an easy way to unmarshal those records into struct except by using "encoding/csv" package for reading record and then doing something like
record, _ := reader.Read()
test := Test{record[0],record[1],atoi(record[2])}
答案1
得分: 40
这是一个示例代码,使用gocarina/gocsv库来处理自定义结构,类似于encoding/json的方式。
你还可以为特定类型编写自定义的编组器和解组器。
示例代码如下:
type Client struct {
Id string `csv:"client_id"` // .csv列标题
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)
}
}
这段代码使用gocsv.UnmarshalFile
函数将CSV文件解组到clients
切片中,并打印每个客户的姓名。
英文:
There is gocarina/gocsv which handles custom struct in the same way encoding/json does.
You can also write custom marshaller and unmarshaller for specific types.
Example:
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)
}
}
答案2
得分: 15
似乎我已经完成了将CSV记录自动编组为结构体(仅限于字符串和整数)的工作。希望这对你有用。
这是一个链接到playground的示例: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包。
英文:
Seems I've done with automatic marshaling of CSV records into structs (limited to string and int). Hope this would be useful.
Here is a link to playground: 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
}
I'll try to create github package is someone needs this implementation.
答案3
得分: 1
你可以自己编写代码。也许可以像这样:
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)
}
这段代码定义了一个名为Test
的结构体,包含Name
、Surname
和Age
三个字段。它还定义了两个方法:String()
用于将结构体转换为字符串,Parse()
用于将字符串解析为结构体。在main()
函数中,创建了一个john
对象,并对其进行打印输出。然后,将john
对象转换为字符串并打印输出。接下来,创建了一个新的rebornJohn
对象,并使用Parse()
方法将字符串解析为结构体,并对其进行打印输出。
英文:
You could bake your own. Perhaps something like this:
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)
}
答案4
得分: 1
在您的情况下,可以这样做:
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)
}
英文:
Using csvutil it is possible to give column header see example.
In your case, this could be :
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)
}
答案5
得分: 0
一种简单的解决方法是使用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
}
上述代码与标准csv库生成的[]string
数据兼容。
英文:
A simple way to solve the problem is to use JSON as an intermediate representation.
Once you've done this, you have a variety of tools at your disposal.
You can...
- Unmarshal directly to your type (if it's all strings)
- Unmarshal to a
map[string]interface{}
and then make any necessary type conversions - Unmarshal -> convert types -> remarshal JSON -> unmarshal to your type
Here's a simple generic marshalling function that enables this flow...
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 above is compatible with the []string
data produced by the standard csv library.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论