-1

例如,我们有 3 个 CSV 文件,通用的是电子邮件列。在第一个文件中是名称和电子邮件,在另一个文件中是电子邮件(加上不同的信息)并且没有名称字段。因此,如果我需要根据第一个文件中的名称和 Еmail 的对应关系填写 2 和 3 文件字段名称,则比...我编写的代码如下:

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "path/filepath"
    "strings"

    "github.com/jszwec/csvutil"
)

type User struct {
    Name  string `csv:"name"`
    Email string `csv:"email"`
}

type Good struct {
    User
    Dt string `csv:"details"`
}

type Strange struct {
    User
    St string `csv:"status"`
    Dt string `csv:"details"`
}

var lst map[string]string

func readCSV(fn string, dat interface{}) error {
    raw, err := ioutil.ReadFile(fn)
    if err != nil {
        return fmt.Errorf("Cannot read CSV: %w", err)
    }

    if err := csvutil.Unmarshal(raw, dat); err != nil {
        return fmt.Errorf("Cannot unmarshal CSV: %w", err)
    }
    return nil
}

func fixNames(fl string, in interface{}) error {
    if err := readCSV(fl, in); err != nil {
        return fmt.Errorf("CSV: %w", err)
    }
    switch in.(type) {
    case *[]Good:
        var vals []Good
        for _, v := range *in.(*[]Good) {
            v.Name = lst[strings.TrimSpace(strings.ToLower(v.Email))]
            vals = append(vals, v)
        }
        in = vals
    case *[]Strange:
        var vals []Strange
        for _, v := range *in.(*[]Strange) {
            v.Name = lst[strings.TrimSpace(strings.ToLower(v.Email))]
            vals = append(vals, v)
        }
        in = vals
    }

    b, err := csvutil.Marshal(in)
    if err != nil {
        return fmt.Errorf("Cannot marshal CSV: %w", err)
    }
    ext := filepath.Ext(fl)
    bas := filepath.Base(fl)
    err = ioutil.WriteFile(bas[:len(bas)-len(ext)]+"-XIAOSE"+ext, b, 0644)
    if err != nil {
        return fmt.Errorf("Cannot save CSV: %w", err)
    }
    return nil
}

func main() {
    var users []User
    if err := readCSV("./Guitar_Contacts.csv", &users); err != nil {
        log.Fatalf("CSV: %s", err)
    }
    lst = make(map[string]string)
    for _, v := range users {
        lst[strings.TrimSpace(strings.ToLower(v.Email))] = v.Name
    }

    var usersGood []Good
    if err := fixNames("./Guitar-Good.csv", &usersGood); err != nil {
        log.Fatalf("fix: %s", err)
    }

    var usersStrange []Strange
    if err := fixNames("./Guitar-Uknown.csv", &usersStrange); err != nil {
        log.Fatalf("fix: %s", err)
    }

    fmt.Println("OK")
}

在这段代码中,我不喜欢 func fixNames 的一部分,开关在哪里:

switch in.(type) {
    case *[]Good:
        var vals []Good
        for _, v := range *in.(*[]Good) {
            v.Name = lst[strings.TrimSpace(strings.ToLower(v.Email))]
            vals = append(vals, v)
        }
        in = vals
    case *[]Strange:
        var vals []Strange
        for _, v := range *in.(*[]Strange) {
            v.Name = lst[strings.TrimSpace(strings.ToLower(v.Email))]
            vals = append(vals, v)
        }
        in = vals
    }

因为我只是在 *in.( SOME_TYPE ) 部分重复代码。我想要一个循环和一个针对不同类型的操作,名称和电子邮件字段所在的结构......

用反射来做这件事也是一个想法。像这样:

v := reflect.ValueOf(in)
v = v.Elem()
for i := 0; i < v.Len(); i++ {
    fmt.Println(v.Index(i))
}

但我不知道下一步该怎么做,如何为 Name 添加v

4

2 回答 2

1

使用反射包,您可以这样做。

func fixNames(fl string, in interface{}) error {
    //other code

    v := reflect.ValueOf(in)
    if v.Kind() == reflect.Ptr {
        arr := v.Elem()
        fmt.Println(arr.Len())
        if arr.Kind() == reflect.Slice || arr.Kind() == reflect.Array {
            for i := 0; i < arr.Len(); i++ {
                elem := arr.Index(i)
                f := elem.FieldByName("Name")
                f.SetString("NameOfUser")
            }
        }
    }

    // other code
}

还有游乐场示例: https: //play.golang.org/p/KrGvLVprslH

于 2020-06-05T10:32:41.290 回答
1

对于这种特殊情况,您不需要反思。您可以通过意识到您只处理User结构的一部分来清理代码,并且您可以简化类型切换:

fix:=func(in *User) {
  in.Name = lst[strings.TrimSpace(strings.ToLower(in.Email))]
}
switch k:=in.(type) {
  case *[]Good:
     for i := range *k {
         fix( &(*k)[i].User )
     }
  case *[]Strange:
     for i := range *k {
         fix( &(*k)[i].User )
     }
}

您必须重复 for 循环,但上面的代码会进行适当的更正。

您可以通过不传递对切片的引用来进行更多清理。

于 2020-06-04T23:28:32.947 回答