Upstream gocarina/gocsv state of the touched files before c31659ffab
This commit is contained in:
1 parent
90a1c52508
commit
a5ece792f8
3 files changed
+363
No files matched your search
@@ -0,0 +1,213 @@
|
|||||||
|
Go CSV
|
||||||
|
=====
|
||||||
|
|
||||||
|
The GoCSV package aims to provide easy serialization and deserialization functions to use CSV in Golang
|
||||||
|
|
||||||
|
API and techniques inspired from https://godoc.org/gopkg.in/mgo.v2
|
||||||
|
|
||||||
|
[](https://godoc.org/github.com/gocarina/gocsv)
|
||||||
|
[](https://travis-ci.org/gocarina/gocsv)
|
||||||
|
|
||||||
|
Installation
|
||||||
|
=====
|
||||||
|
|
||||||
|
```go get -u github.com/gocarina/gocsv```
|
||||||
|
|
||||||
|
Full example
|
||||||
|
=====
|
||||||
|
|
||||||
|
Consider the following CSV file
|
||||||
|
|
||||||
|
```csv
|
||||||
|
|
||||||
|
client_id,client_name,client_age
|
||||||
|
1,Jose,42
|
||||||
|
2,Daniel,26
|
||||||
|
3,Vincent,32
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Easy binding in Go!
|
||||||
|
---
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gocarina/gocsv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NotUsed struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct { // Our example struct, you can use "-" to ignore a field
|
||||||
|
Id string `csv:"client_id"`
|
||||||
|
Name string `csv:"client_name"`
|
||||||
|
Age string `csv:"client_age"`
|
||||||
|
NotUsedString string `csv:"-"`
|
||||||
|
NotUsedStruct NotUsed `csv:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
clientsFile, err := os.OpenFile("clients.csv", os.O_RDWR|os.O_CREATE, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer clientsFile.Close()
|
||||||
|
|
||||||
|
clients := []*Client{}
|
||||||
|
|
||||||
|
if err := gocsv.UnmarshalFile(clientsFile, &clients); err != nil { // Load clients from file
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, client := range clients {
|
||||||
|
fmt.Println("Hello", client.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := clientsFile.Seek(0, 0); err != nil { // Go to the start of the file
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clients = append(clients, &Client{Id: "12", Name: "John", Age: "21"}) // Add clients
|
||||||
|
clients = append(clients, &Client{Id: "13", Name: "Fred"})
|
||||||
|
clients = append(clients, &Client{Id: "14", Name: "James", Age: "32"})
|
||||||
|
clients = append(clients, &Client{Id: "15", Name: "Danny"})
|
||||||
|
csvContent, err := gocsv.MarshalString(&clients) // Get all clients as CSV string
|
||||||
|
//err = gocsv.MarshalFile(&clients, clientsFile) // Use this to save the CSV back to the file
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(csvContent) // Display all clients as CSV string
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Customizable Converters
|
||||||
|
---
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
type DateTime struct {
|
||||||
|
time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the internal date as CSV string
|
||||||
|
func (date *DateTime) MarshalCSV() (string, error) {
|
||||||
|
return date.Time.Format("20060201"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// You could also use the standard Stringer interface
|
||||||
|
func (date *DateTime) String() (string) {
|
||||||
|
return date.String() // Redundant, just for example
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the CSV string as internal date
|
||||||
|
func (date *DateTime) UnmarshalCSV(csv string) (err error) {
|
||||||
|
date.Time, err = time.Parse("20060201", csv)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct { // Our example struct with a custom type (DateTime)
|
||||||
|
Id string `csv:"id"`
|
||||||
|
Name string `csv:"name"`
|
||||||
|
Employed DateTime `csv:"employed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Nested structs
|
||||||
|
---
|
||||||
|
|
||||||
|
When decoding nested pointer structs, GoCSV allocates the pointers needed to
|
||||||
|
reach each field. An `omitempty` field still omits an empty leaf pointer value;
|
||||||
|
it does not prevent allocation of the enclosing structs.
|
||||||
|
|
||||||
|
By default, the fields of nested structs are prefixed with the parent field's
|
||||||
|
name. For example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Foo struct {
|
||||||
|
B Bar
|
||||||
|
X int `csv:"x"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bar struct {
|
||||||
|
A int `csv:"a"`
|
||||||
|
B int `csv:"b"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
produces columns `B.a, B.b, x`.
|
||||||
|
|
||||||
|
Use the `csv:"."` tag to inline the nested struct's fields without that prefix; for example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Foo struct {
|
||||||
|
B Bar `csv:"."`
|
||||||
|
X int `csv:"x"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bar struct {
|
||||||
|
A int `csv:"a"`
|
||||||
|
B int `csv:"b"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
produces columns `a, b, x`.
|
||||||
|
|
||||||
|
Customizable CSV Reader / Writer
|
||||||
|
---
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
...
|
||||||
|
|
||||||
|
gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
|
||||||
|
r := csv.NewReader(in)
|
||||||
|
r.Comma = '|'
|
||||||
|
return r // Allows use pipe as delimiter
|
||||||
|
})
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
|
||||||
|
r := csv.NewReader(in)
|
||||||
|
r.LazyQuotes = true
|
||||||
|
r.Comma = '.'
|
||||||
|
return r // Allows use dot as delimiter and use quotes in CSV
|
||||||
|
})
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
|
||||||
|
//return csv.NewReader(in)
|
||||||
|
return gocsv.LazyCSVReader(in) // Allows use of quotes in CSV
|
||||||
|
})
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
gocsv.UnmarshalFile(file, &clients)
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
gocsv.SetCSVWriter(func(out io.Writer) *gocsv.SafeCSVWriter {
|
||||||
|
writer := csv.NewWriter(out)
|
||||||
|
writer.Comma = '|'
|
||||||
|
return gocsv.NewSafeCSVWriter(writer)
|
||||||
|
})
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
gocsv.MarshalFile(&clients, file)
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gocarina/gocsv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NotUsed struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct { // Our example struct, you can use "-" to ignore a field
|
||||||
|
ID string `csv:"client_id"`
|
||||||
|
Name string `csv:"client_name"`
|
||||||
|
Age string `csv:"client_age"`
|
||||||
|
NotUsedString string `csv:"-"`
|
||||||
|
NotUsedStruct NotUsed `csv:"-"`
|
||||||
|
Address1 Address `csv:"addr1"`
|
||||||
|
Address2 Address //`csv:"addr2"` will use Address2 in header
|
||||||
|
Employed DateTime `csv:"employed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Address struct {
|
||||||
|
Street string `csv:"street"`
|
||||||
|
City string `csv:"city"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ gocsv.TypeMarshaller = new(DateTime)
|
||||||
|
var _ gocsv.TypeUnmarshaller = new(DateTime)
|
||||||
|
|
||||||
|
type DateTime struct {
|
||||||
|
time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the internal date as CSV string
|
||||||
|
func (date *DateTime) MarshalCSV() (string, error) {
|
||||||
|
return date.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// You could also use the standard Stringer interface
|
||||||
|
func (date DateTime) String() string {
|
||||||
|
return date.Time.Format("20060201")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the CSV string as internal date
|
||||||
|
func (date *DateTime) UnmarshalCSV(csv string) (err error) {
|
||||||
|
date.Time, err = time.Parse("20060201", csv)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// set the pipe as the delimiter for writing
|
||||||
|
gocsv.TagSeparator = "|"
|
||||||
|
// set the pipe as the delimiter for reading
|
||||||
|
gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
|
||||||
|
r := csv.NewReader(in)
|
||||||
|
r.Comma = '|'
|
||||||
|
return r
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create an empty clients file
|
||||||
|
clientsFile, err := os.OpenFile("clients.csv", os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer clientsFile.Close()
|
||||||
|
|
||||||
|
// Create clients
|
||||||
|
clients := []*Client{
|
||||||
|
{ID: "12", Name: "John", Age: "21",
|
||||||
|
Address1: Address{"Street 1", "City1"},
|
||||||
|
Address2: Address{"Street 2", "City2"},
|
||||||
|
Employed: DateTime{time.Date(2022, 11, 04, 12, 0, 0, 0, time.UTC)},
|
||||||
|
},
|
||||||
|
{ID: "13", Name: "Fred",
|
||||||
|
Address1: Address{`Main "Street" 1`, "City1"}, // show quotes in value
|
||||||
|
Address2: Address{"Main Street 2", "City2"},
|
||||||
|
Employed: DateTime{time.Date(2022, 11, 04, 13, 0, 0, 0, time.UTC)},
|
||||||
|
},
|
||||||
|
{ID: "14", Name: "James", Age: "32",
|
||||||
|
Address1: Address{"Center Street 1", "City1"},
|
||||||
|
Address2: Address{"Center Street 2", "City2"},
|
||||||
|
Employed: DateTime{time.Date(2022, 11, 04, 14, 0, 0, 0, time.UTC)},
|
||||||
|
},
|
||||||
|
{ID: "15", Name: "Danny",
|
||||||
|
Address1: Address{"State Street 1", "City1"},
|
||||||
|
Address2: Address{"State Street 2", "City2"},
|
||||||
|
Employed: DateTime{time.Date(2022, 11, 04, 15, 0, 0, 0, time.UTC)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Save clients to csv file
|
||||||
|
if err = gocsv.MarshalFile(&clients, clientsFile); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the file reader
|
||||||
|
if _, err := clientsFile.Seek(0, io.SeekStart); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// Read file and print to console
|
||||||
|
b, err := ioutil.ReadAll(clientsFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("clients.csv:")
|
||||||
|
fmt.Println(string(b))
|
||||||
|
|
||||||
|
// Reset the file reader
|
||||||
|
if _, err := clientsFile.Seek(0, io.SeekStart); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// Load clients from file
|
||||||
|
var newClients []*Client
|
||||||
|
if err := gocsv.UnmarshalFile(clientsFile, &newClients); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("clients:")
|
||||||
|
for _, c := range newClients {
|
||||||
|
fmt.Printf("%s:%s Adress1:%s, %s Address2:%s, %s Employed: %s\n",
|
||||||
|
c.ID, c.Name,
|
||||||
|
c.Address1.Street, c.Address1.City,
|
||||||
|
c.Address2.Street, c.Address2.City,
|
||||||
|
c.Employed,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func Example() {
|
||||||
|
main()
|
||||||
|
// Output:
|
||||||
|
// clients.csv:
|
||||||
|
// client_id|client_name|client_age|addr1.street|addr1.city|Address2.street|Address2.city|employed
|
||||||
|
// 12|John|21|Street 1|City1|Street 2|City2|20220411
|
||||||
|
// 13|Fred||"Main ""Street"" 1"|City1|Main Street 2|City2|20220411
|
||||||
|
// 14|James|32|Center Street 1|City1|Center Street 2|City2|20220411
|
||||||
|
// 15|Danny||State Street 1|City1|State Street 2|City2|20220411
|
||||||
|
//
|
||||||
|
// clients:
|
||||||
|
// 12:John Adress1:Street 1, City1 Address2:Street 2, City2 Employed: 20220411
|
||||||
|
// 13:Fred Adress1:Main "Street" 1, City1 Address2:Main Street 2, City2 Employed: 20220411
|
||||||
|
// 14:James Adress1:Center Street 1, City1 Address2:Center Street 2, City2 Employed: 20220411
|
||||||
|
// 15:Danny Adress1:State Street 1, City1 Address2:State Street 2, City2 Employed: 20220411
|
||||||
|
}
|
||||||
Reference in new issue
Block a user