csv support
parent
c3ca057b96
commit
fd5cf2c1f6
@ -0,0 +1,261 @@
|
|||||||
|
package sysconf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CSV struct {
|
||||||
|
header []string
|
||||||
|
text [][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CSVRow struct {
|
||||||
|
header []string
|
||||||
|
data []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CSVValue struct {
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseCSV(data []byte, hasHeader bool) (csv CSV, err error) {
|
||||||
|
strData := strings.Split(string(bytes.TrimSpace(data)), "\n")
|
||||||
|
if len(strData) < 1 {
|
||||||
|
err = fmt.Errorf("cannot parse data,invalid data format")
|
||||||
|
}
|
||||||
|
var header []string
|
||||||
|
var text [][]string
|
||||||
|
if hasHeader {
|
||||||
|
header = csvAnalyse(strData[0])
|
||||||
|
strData = strData[1:]
|
||||||
|
} else {
|
||||||
|
num := len(csvAnalyse(strData[0]))
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
|
header = append(header, strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range strData {
|
||||||
|
tmpData := csvAnalyse(v)
|
||||||
|
if len(tmpData) != len(header) {
|
||||||
|
err = fmt.Errorf("cannot parse data line %d,got %d values but need %d", k, len(tmpData), len(header))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
text = append(text, tmpData)
|
||||||
|
}
|
||||||
|
csv.header = header
|
||||||
|
csv.text = text
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSV) Header() []string {
|
||||||
|
return csv.header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSV) Data() [][]string {
|
||||||
|
return csv.text
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSV) Row(row int) *CSVRow {
|
||||||
|
if row >= len(csv.Data()) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &CSVRow{
|
||||||
|
header: csv.Header(),
|
||||||
|
data: csv.Data()[row],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVRow) Get(key string) *CSVValue {
|
||||||
|
for k, v := range csv.header {
|
||||||
|
if v == key {
|
||||||
|
return &CSVValue{
|
||||||
|
key: key,
|
||||||
|
value: csv.data[k],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVRow) Col(key int) *CSVValue {
|
||||||
|
if key >= len(csv.header) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &CSVValue{
|
||||||
|
key: csv.header[key],
|
||||||
|
value: csv.data[key],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVRow) Header() []string {
|
||||||
|
return csv.header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSV) MapData() []map[string]string {
|
||||||
|
var result []map[string]string
|
||||||
|
for _, v := range csv.text {
|
||||||
|
tmp := make(map[string]string)
|
||||||
|
for k, v2 := range csv.header {
|
||||||
|
tmp[v2] = v[k]
|
||||||
|
}
|
||||||
|
result = append(result, tmp)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func CsvAnalyse(data string) []string {
|
||||||
|
return csvAnalyse(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func csvAnalyse(data string) []string {
|
||||||
|
var segStart bool = false
|
||||||
|
var segReady bool = false
|
||||||
|
var segSign string = ""
|
||||||
|
var dotReady bool = false
|
||||||
|
data = strings.TrimSpace(data)
|
||||||
|
var result []string
|
||||||
|
var seg string
|
||||||
|
for k, v := range []rune(data) {
|
||||||
|
if k == 0 && v != []rune(`"`)[0] {
|
||||||
|
dotReady = true
|
||||||
|
}
|
||||||
|
if v != []rune(`,`)[0] && dotReady {
|
||||||
|
segSign = `,`
|
||||||
|
segStart = true
|
||||||
|
dotReady = false
|
||||||
|
if v == []rune(`"`)[0] {
|
||||||
|
segSign = `"`
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dotReady && v == []rune(`,`)[0] {
|
||||||
|
//dotReady = false
|
||||||
|
result = append(result, "")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if v == []rune(`"`)[0] && segStart {
|
||||||
|
if !segReady {
|
||||||
|
segReady = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seg += `"`
|
||||||
|
segReady = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if segReady && segSign == `"` && segStart {
|
||||||
|
segReady = false
|
||||||
|
segStart = false
|
||||||
|
result = append(result, seg)
|
||||||
|
segSign = ``
|
||||||
|
seg = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if v == []rune(`"`)[0] && !segStart {
|
||||||
|
segStart = true
|
||||||
|
segReady = false
|
||||||
|
segSign = `"`
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v == []rune(`,`)[0] && !segStart {
|
||||||
|
dotReady = true
|
||||||
|
}
|
||||||
|
if v == []rune(`,`)[0] && segStart && segSign == "," {
|
||||||
|
segStart = false
|
||||||
|
result = append(result, seg)
|
||||||
|
dotReady = true
|
||||||
|
segSign = ``
|
||||||
|
seg = ""
|
||||||
|
}
|
||||||
|
if segStart {
|
||||||
|
seg = string(append([]rune(seg), v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(data) != 0 && len(result) == 0 && seg == "" {
|
||||||
|
result = append(result, data)
|
||||||
|
} else {
|
||||||
|
result = append(result, seg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func MarshalCSV(header []string, ins interface{}) ([]byte, error) {
|
||||||
|
var result [][]string
|
||||||
|
t := reflect.TypeOf(ins)
|
||||||
|
v := reflect.ValueOf(ins)
|
||||||
|
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
|
||||||
|
return nil, errors.New("not a Slice or Array")
|
||||||
|
}
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
subT := reflect.TypeOf(v.Index(i).Interface())
|
||||||
|
subV := reflect.ValueOf(v.Index(i).Interface())
|
||||||
|
if subV.Kind() == reflect.Slice || subV.Kind() == reflect.Array {
|
||||||
|
if subT.Kind() == reflect.Ptr {
|
||||||
|
subV = subV.Elem()
|
||||||
|
}
|
||||||
|
var tmp []string
|
||||||
|
for j := 0; j < subV.Len(); j++ {
|
||||||
|
tmp = append(tmp, fmt.Sprint(reflect.ValueOf(subV.Index(j))))
|
||||||
|
}
|
||||||
|
result = append(result, tmp)
|
||||||
|
}
|
||||||
|
if subV.Kind() == reflect.Struct {
|
||||||
|
var tmp []string
|
||||||
|
if subT.Kind() == reflect.Ptr {
|
||||||
|
subV = subV.Elem()
|
||||||
|
}
|
||||||
|
for i := 0; i < subV.NumField(); i++ {
|
||||||
|
tmp = append(tmp, fmt.Sprint(subV.Field(i)))
|
||||||
|
}
|
||||||
|
result = append(result, tmp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildCSV(header,result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildCSV(header []string, data [][]string) ([]byte, error) {
|
||||||
|
var result []string
|
||||||
|
var length int
|
||||||
|
build := func(slc []string) string {
|
||||||
|
for k, v := range slc {
|
||||||
|
if strings.Index(v, `"`) >= 0 {
|
||||||
|
v = strings.ReplaceAll(v, `"`, `""`)
|
||||||
|
}
|
||||||
|
if strings.Index(v,"\n")>=0 {
|
||||||
|
v=strings.ReplaceAll(v,"\n",`\n`)
|
||||||
|
}
|
||||||
|
if strings.Index(v,"\r")>=0 {
|
||||||
|
v=strings.ReplaceAll(v,"\r",`\r`)
|
||||||
|
}
|
||||||
|
v = `"` + v + `"`
|
||||||
|
slc[k] = v
|
||||||
|
}
|
||||||
|
return strings.Join(slc, ",")
|
||||||
|
}
|
||||||
|
if len(header) != 0 {
|
||||||
|
result = append(result, build(header))
|
||||||
|
length = len(header)
|
||||||
|
} else {
|
||||||
|
length = len(data[0])
|
||||||
|
}
|
||||||
|
for k, v := range data {
|
||||||
|
if len(v) != length {
|
||||||
|
return nil, fmt.Errorf("line %d got length %d ,but need %d", k, len(v), length)
|
||||||
|
}
|
||||||
|
result = append(result, build(v))
|
||||||
|
}
|
||||||
|
return []byte(strings.Join(result, "\n")), nil
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package sysconf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_csv(t *testing.T) {
|
||||||
|
//var test Sqlplus
|
||||||
|
var text=`
|
||||||
|
姓名,班级,性别,年龄
|
||||||
|
张三,"我,不""知道",boy,23
|
||||||
|
"里斯","哈哈",girl,23
|
||||||
|
`
|
||||||
|
fmt.Println(csvAnalyse(`请求权,lkjdshck,dsvdsv,"sdvkjsdv,",=dsvdsv,"=,dsvsdv"`))
|
||||||
|
a,b:=ParseCSV([]byte(text),true)
|
||||||
|
fmt.Println(b)
|
||||||
|
fmt.Println(a.Row(0).Col(3).MustInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
type csvtest struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
}
|
||||||
|
func Test_Masharl(t *testing.T) {
|
||||||
|
//var test Sqlplus
|
||||||
|
/*
|
||||||
|
var a []csvtest = []csvtest{
|
||||||
|
{"lala",1},
|
||||||
|
{"haha",34},
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
var a [][]string
|
||||||
|
a=append(a,[]string{"a","b","c"})
|
||||||
|
a=append(a,[]string{"1",`s"s"d`,"3"})
|
||||||
|
b,_:=MarshalCSV([]string{},a)
|
||||||
|
fmt.Println(string(b))
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
package sysconf
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func (csv *CSVValue)Key()string {
|
||||||
|
return csv.key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)Int()(int,error) {
|
||||||
|
tmp,err:=strconv.Atoi(csv.value)
|
||||||
|
return tmp,err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)MustInt()int {
|
||||||
|
tmp,err:=csv.Int()
|
||||||
|
if err!=nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)Int64()(int64,error) {
|
||||||
|
tmp,err:=strconv.ParseInt(csv.value,10,64)
|
||||||
|
return tmp,err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)MustInt64()int64 {
|
||||||
|
tmp,err:=csv.Int64()
|
||||||
|
if err!=nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)Int32()(int32,error) {
|
||||||
|
tmp,err:=strconv.ParseInt(csv.value,10,32)
|
||||||
|
return int32(tmp),err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)MustInt32()int32 {
|
||||||
|
tmp,err:=csv.Int32()
|
||||||
|
if err!=nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)Uint64()(uint64,error) {
|
||||||
|
tmp,err:=strconv.ParseUint(csv.value,10,64)
|
||||||
|
return tmp,err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)MustUint64()uint64 {
|
||||||
|
tmp,err:=csv.Uint64()
|
||||||
|
if err!=nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)Uint32()(uint32,error) {
|
||||||
|
tmp,err:=strconv.ParseUint(csv.value,10,32)
|
||||||
|
return uint32(tmp),err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)MustUint32()uint32 {
|
||||||
|
tmp,err:=csv.Uint32()
|
||||||
|
if err!=nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)String()string {
|
||||||
|
return csv.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)Byte()[]byte {
|
||||||
|
return []byte(csv.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (csv *CSVValue)Bool()(bool,error) {
|
||||||
|
tmp,err:=strconv.ParseBool(csv.value)
|
||||||
|
return tmp,err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)MustBool()bool {
|
||||||
|
tmp,err:=csv.Bool()
|
||||||
|
if err!=nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)Float64()(float64,error) {
|
||||||
|
tmp,err:=strconv.ParseFloat(csv.value,64)
|
||||||
|
return tmp,err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)MustFloat64()float64 {
|
||||||
|
tmp,err:=csv.Float64()
|
||||||
|
if err!=nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)Float32()(float32,error) {
|
||||||
|
tmp,err:=strconv.ParseFloat(csv.value,32)
|
||||||
|
return float32(tmp),err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (csv *CSVValue)MustFloat32()float32 {
|
||||||
|
tmp,err:=csv.Float32()
|
||||||
|
if err!=nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
Loading…
Reference in New Issue