我有一个文件,其中包含许多类型的数据记录,我需要将它们解析为结构。
如果能以一种记录方式填充结构的惯用方式(如果存在的话),我将不胜感激。类似于python的namedtuple(*fields)
构造函数。
package main
import (
"fmt"
"strconv"
"strings"
)
type X interface{}
type HDR struct {
typer, a string
b int
}
type BDY struct {
typer, c string
d int
e string
}
var lines string = `HDR~two~5
BDY~four~6~five`
func sn(s string) int {
i, _ := strconv.Atoi(s)
return i
}
func main() {
sl := strings.Split(lines, "\n")
for _, l := range sl {
fields := strings.Split(l, "~")
var r X
switch fields[0] {
case "HDR":
r = HDR{fields[0], fields[1], sn(fields[2])} // 1
case "BDY":
r = BDY{fields[0], fields[1], sn(fields[2]), fields[3]} // 2
}
fmt.Printf("%T : %v\n", r, r)
}
}
我特别想了解标记为// 1
和// 2
的行是否可以用代码方便地替换,也许是某种通用解码器,它允许结构本身进行类型转换。
答案 0 :(得分:2)
使用reflect包以编程方式设置字段。
由反射包设置的字段必须为exported。通过使用名称中的第一条符文大写来导出名称:
type HDR struct {
Typer, A string
B int
}
type BDY struct {
Typer, C string
D int
E string
}
创建名称映射到与名称关联的类型:
var types = map[string]reflect.Type{
"HDR": reflect.TypeOf((*HDR)(nil)).Elem(),
"BDY": reflect.TypeOf((*BDY)(nil)).Elem(),
}
对于每一行,使用types
映射创建一个类型的值:
for _, l := range strings.Split(lines, "\n") {
fields := strings.Split(l, "~")
t := types[fields[0]]
v := reflect.New(t).Elem()
...
}
将光标放在行中的字段上。获取字段值,将字符串转换为字段值的种类并设置字段值:
for i, f := range fields {
fv := v.Field(i)
switch fv.Type().Kind() {
case reflect.String:
fv.SetString(f)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n, _ := strconv.ParseInt(f, 10, fv.Type().Bits())
fv.SetInt(n)
}
}
这是该方法的基本概述。错误处理令人难以置信:如果类型名称不是types
中提到的类型之一,则应用程序将惊慌。应用程序将忽略解析整数返回的错误;如果数据中的字段多于结构,则应用程序将感到恐慌;遇到不受支持的字段类型时,应用程序不报告错误;还有更多