将结构转换为CSV字符串

tquggr8v  于 2023-03-15  发布在  其他
关注(0)|答案(3)|浏览(156)

我有一个在DB响应后被扫描的结构体,如下所示。每个字段都是相同的len()。我想使用这个结构体并生成一个CSV分隔的字符串/

package main

import "fmt"

type Data struct {
    id   []string
    col1 []float64
    col2 []float64
}

func main() {
    d := &Data{
        id:   []string{"id_1", "id_1", "id_1", "id_1"},
        col1: []float64{340.384926, 321.385028, 520.341473, 500.385473},
        col2: []float64{123.285031, 4087.284675, -8958.284216, -7612.283668},
    }
    fmt.Printf("%+v", d)
}

我想循环遍历这个结构,我相信使用reflect是可行的,然后构造一个CSV字符串,如下所示,使用结构字段名作为标题,值作为该标题的单独列,用逗号分隔。

`
id,col1,col2
id_1,340.384926,123.285031
id_1,321.385028,4087.284675
id_1,520.341473,-8958.284216
id_1,500.385473,-7612.283668
`

实现这一目标的有效方法是什么?

cgfeq70w

cgfeq70w1#

如果可能的话,避免使用reflect迭代struct,因为它会导致 * 性能降低 * 和 * 代码可读性降低 *。不要落入XY problem的圈套-这里的要求是将Data struct转换为csv字符串(Y问题),但这里的X问题是避免使用Data这样的struct类型作为起点。
许多操作csv的golang包更喜欢:

但是,如果Data类型是不可避免的,则可以先编写一个函数,将Data转换为[][]string,同时避免使用reflect

func TransformDataTo2DSlice(d Data) [][]string {

    numRows := len(d.id)
    result := make([][]string, numRows+1)

    // Add header row
    result[0] = []string{"id", "col1", "col2"}

    // Add data rows
    for i := 0; i < numRows; i++ {
        result[i+1] = []string{d.id[i],
            strconv.FormatFloat(d.col1[i], 'f', -1, 64),
            strconv.FormatFloat(d.col2[i], 'f', -1, 64),
        }
    }

    return result
}

接下来,从encoding/csv使用w.WriteAll()方法轻松地将[][]string转换为csv

func main() {

    d := Data{
        id:   []string{"id_1", "id_1", "id_1", "id_1"},
        col1: []float64{340.384926, 321.385028, 520.341473, 500.385473},
        col2: []float64{123.285031, 4087.284675, -8958.284216, -7612.283668},
    }

    d2dslice := TransformDataTo2DSlice(d)

    // fmt.Printf("%+v", d2dslice)
    // [[id, col1, col2],
    // [id_1, 340.384926, 123.285031],
    // [id_1, 321.385028, 4087.284675],
    // [id_1, 520.341473, -8958.284216],
    // [id_1,500.385473,-7612.283668]]

    w := csv.NewWriter(os.Stdout)
    w.WriteAll(d2dslice)

    if err := w.Error(); err != nil {
        log.Fatalln("error writing csv:", err)
    }

    // stdout:
    // id,col1,col2
    // id_1,340.384926,123.285031
    // id_1,321.385028,4087.284675
    // id_1,520.341473,-8958.284216
    // id_1,500.385473,-7612.283668
}

运行上面的程序:go-playground

1cosmwyk

1cosmwyk3#

  • 步骤1:将数据结构转换为二维数组[][]string
  • 步骤2:打印二维数组的结果。
package main

import (
    "fmt"
    "log"
    "reflect"
)

type Data struct {
    Id   []string  `json:"id"`
    Col1 []float64 `json:"col1"`
    Col2 []float64 `json:"col2"`
}

func (d *Data) String() string {
    dataType := reflect.TypeOf(*d)
    dataValue := reflect.ValueOf(*d)

    // Step 1: Convert the data structure into a 2-dimensional array.
    var result [][]string // [col][how many 'row' on each col]
    for col := 0; col < dataType.NumField(); col++ {
        filedType := dataType.Field(col)
        filedValue := dataValue.Field(col)

        if result == nil {
            colMax := dataType.NumField()
            result = make([][]string, colMax)
        }

        var rowMax int
        switch filedValue.Interface().(type) {
        case []string:
            rowMax = len(filedValue.Interface().([]string))
        case []float64:
            rowMax = len(filedValue.Interface().([]float64))
        default:
            log.Fatalf("not support type. %v", filedValue.Interface())
        }
        if result[col] == nil {
            result[col] = make([]string, rowMax+1) // 1 for header
        }

        // header
        result[col][0] = fmt.Sprintf("%v", filedType.Tag.Get("json"))

        // data
        var cell any
        for row := 1; row <= rowMax; row++ {
            switch filedValue.Interface().(type) {
            case []string:
                cell = filedValue.Interface().([]string)[row-1]
            case []float64:
                cell = filedValue.Interface().([]float64)[row-1]
            default:
                log.Fatalf("not support type. %v", filedValue.Interface())
            }
            result[col][row] = fmt.Sprintf("%v", cell)
        }
    }

    // Step 2: Print the result of the 2-dimensional array.
    str := ""
    const sep = ","
    for row := 0; row < len(result[0]); row++ {
        for col := 0; col < len(result); col++ {
            str += fmt.Sprintf("%v", result[col][row])
            if col < len(result)-1 {
                str += sep
            }
        }
        if row < len(result[0])-1 {
            str += "\n"
        }
    }
    return str
}

func main() {
    d := &Data{
        Id:   []string{"id_1", "id_1", "id_1", "id_1"},
        Col1: []float64{340.384926, 321.385028, 520.341473, 500.385473},
        Col2: []float64{123.285031, 4087.284675, -8958.284216, -7612.283668},
    }
    fmt.Println(d)
}

输出:

id,col1,col2
id_1,340.384926,123.285031  
id_1,321.385028,4087.284675 
id_1,520.341473,-8958.284216
id_1,500.385473,-7612.283668

go-playground

相关问题