{"id":13413802,"url":"https://github.com/jszwec/csvutil","last_synced_at":"2026-01-31T11:07:32.452Z","repository":{"id":27068638,"uuid":"108800571","full_name":"jszwec/csvutil","owner":"jszwec","description":"csvutil provides fast and idiomatic mapping between CSV and Go (golang) values.","archived":false,"fork":false,"pushed_at":"2024-08-21T03:55:48.000Z","size":398,"stargazers_count":913,"open_issues_count":0,"forks_count":59,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-08-21T04:57:48.123Z","etag":null,"topics":["csv","csv-parser","csvutil","decoder","fast","go","gocsv","golang","marshal","std-csv","streaming","unmarshal"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jszwec.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-30T04:09:48.000Z","updated_at":"2024-08-21T03:55:50.000Z","dependencies_parsed_at":"2024-10-26T09:44:41.621Z","dependency_job_id":"a509f51d-79ce-4433-ba9f-08a6d2467ec5","html_url":"https://github.com/jszwec/csvutil","commit_stats":{"total_commits":141,"total_committers":7,"mean_commits":"20.142857142857142","dds":"0.21276595744680848","last_synced_commit":"53deda7323bf56a3c1918e39cc2ae3940fbba0c6"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/jszwec/csvutil","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jszwec%2Fcsvutil","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jszwec%2Fcsvutil/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jszwec%2Fcsvutil/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jszwec%2Fcsvutil/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jszwec","download_url":"https://codeload.github.com/jszwec/csvutil/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jszwec%2Fcsvutil/sbom","scorecard":{"id":539800,"data":{"date":"2025-08-11","repo":{"name":"github.com/jszwec/csvutil","commit":"b9b849659044c1827e80149317d4aebc69dc918a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.5,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":1,"reason":"Found 4/29 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/go.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/jszwec/csvutil/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/jszwec/csvutil/go.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/go.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/jszwec/csvutil/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/jszwec/csvutil/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:46: update your workflow using https://app.stepsecurity.io/secureworkflow/jszwec/csvutil/go.yml/master?enable=pin","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'","Warn: branch protection not enabled for branch 'v1.2.2'","Warn: branch protection not enabled for branch 'v1.1.1'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 5 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T07:55:54.168Z","repository_id":27068638,"created_at":"2025-08-20T07:55:54.168Z","updated_at":"2025-08-20T07:55:54.168Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28939586,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-31T10:18:23.202Z","status":"ssl_error","status_checked_at":"2026-01-31T10:18:22.693Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csv","csv-parser","csvutil","decoder","fast","go","gocsv","golang","marshal","std-csv","streaming","unmarshal"],"created_at":"2024-07-30T20:01:49.713Z","updated_at":"2026-01-31T11:07:32.434Z","avatar_url":"https://github.com/jszwec.png","language":"Go","readme":"csvutil [![PkgGoDev](https://pkg.go.dev/badge/github.com/jszwec/csvutil@v1.4.0?tab=doc)](https://pkg.go.dev/github.com/jszwec/csvutil?tab=doc) ![Go](https://github.com/jszwec/csvutil/workflows/Go/badge.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/jszwec/csvutil)](https://goreportcard.com/report/github.com/jszwec/csvutil) [![codecov](https://codecov.io/gh/jszwec/csvutil/branch/master/graph/badge.svg)](https://codecov.io/gh/jszwec/csvutil)\n=================\n\n\u003cp align=\"center\"\u003e\n  \u003cimg style=\"float: right;\" src=\"https://user-images.githubusercontent.com/3941256/33054906-52b4bc08-ce4a-11e7-9651-b70c5a47c921.png\"/ width=200\u003e\n\u003c/p\u003e\n\nPackage csvutil provides fast, idiomatic, and dependency free mapping between CSV and Go (golang) values.\n\nThis package is not a CSV parser, it is based on the [Reader](https://godoc.org/github.com/jszwec/csvutil#Reader) and [Writer](https://godoc.org/github.com/jszwec/csvutil#Writer)\ninterfaces which are implemented by eg. std Go (golang) [csv package](https://golang.org/pkg/encoding/csv). This gives a possibility\nof choosing any other CSV writer or reader which may be more performant.\n\nInstallation\n------------\n\n    go get github.com/jszwec/csvutil\n\nRequirements\n-------------\n\n* Go1.18+\n\nIndex\n------\n\n1. [Examples](#examples)\n\t1. [Unmarshal](#examples_unmarshal)\n\t2. [Marshal](#examples_marshal)\n\t3. [Unmarshal and metadata](#examples_unmarshal_and_metadata)\n\t4. [But my CSV file has no header...](#examples_but_my_csv_has_no_header)\n\t5. [Decoder.Map - data normalization](#examples_decoder_map)\n\t6. [Different separator/delimiter](#examples_different_separator)\n\t7. [Custom Types](#examples_custom_types)\n\t8. [Custom time.Time format](#examples_time_format)\n\t9. [Custom struct tags](#examples_struct_tags)\n\t10. [Slice and Map fields](#examples_slice_and_map_field)\n\t11. [Nested/Embedded structs](#examples_nested_structs)\n\t12. [Inline tag](#examples_inlined_structs)\n2. [Performance](#performance)\n\t1. [Unmarshal](#performance_unmarshal)\n\t2. [Marshal](#performance_marshal)\n\nExample \u003ca name=\"examples\"\u003e\u003c/a\u003e\n--------\n\n### Unmarshal \u003ca name=\"examples_unmarshal\"\u003e\u003c/a\u003e\n\nNice and easy Unmarshal is using the Go std [csv.Reader](https://golang.org/pkg/encoding/csv/#Reader) with its default options. Use [Decoder](https://godoc.org/github.com/jszwec/csvutil#Decoder) for streaming and more advanced use cases.\n\n```go\n\tvar csvInput = []byte(`\nname,age,CreatedAt\njacek,26,2012-04-01T15:00:00Z\njohn,,0001-01-01T00:00:00Z`,\n\t)\n\n\ttype User struct {\n\t\tName      string `csv:\"name\"`\n\t\tAge       int    `csv:\"age,omitempty\"`\n\t\tCreatedAt time.Time\n\t}\n\n\tvar users []User\n\tif err := csvutil.Unmarshal(csvInput, \u0026users); err != nil {\n\t\tfmt.Println(\"error:\", err)\n\t}\n\n\tfor _, u := range users {\n\t\tfmt.Printf(\"%+v\\n\", u)\n\t}\n\n\t// Output:\n\t// {Name:jacek Age:26 CreatedAt:2012-04-01 15:00:00 +0000 UTC}\n\t// {Name:john Age:0 CreatedAt:0001-01-01 00:00:00 +0000 UTC}\n```\n\n### Marshal \u003ca name=\"examples_marshal\"\u003e\u003c/a\u003e\n\nMarshal is using the Go std [csv.Writer](https://golang.org/pkg/encoding/csv/#Writer) with its default options. Use [Encoder](https://godoc.org/github.com/jszwec/csvutil#Encoder) for streaming or to use a different Writer.\n\n```go\n\ttype Address struct {\n\t\tCity    string\n\t\tCountry string\n\t}\n\n\ttype User struct {\n\t\tName string\n\t\tAddress\n\t\tAge       int `csv:\"age,omitempty\"`\n\t\tCreatedAt time.Time\n\t}\n\n\tusers := []User{\n\t\t{\n\t\t\tName:      \"John\",\n\t\t\tAddress:   Address{\"Boston\", \"USA\"},\n\t\t\tAge:       26,\n\t\t\tCreatedAt: time.Date(2010, 6, 2, 12, 0, 0, 0, time.UTC),\n\t\t},\n\t\t{\n\t\t\tName:    \"Alice\",\n\t\t\tAddress: Address{\"SF\", \"USA\"},\n\t\t},\n\t}\n\n\tb, err := csvutil.Marshal(users)\n\tif err != nil {\n\t\tfmt.Println(\"error:\", err)\n\t}\n\tfmt.Println(string(b))\n\n\t// Output:\n\t// Name,City,Country,age,CreatedAt\n\t// John,Boston,USA,26,2010-06-02T12:00:00Z\n\t// Alice,SF,USA,,0001-01-01T00:00:00Z\n```\n\n### Unmarshal and metadata \u003ca name=\"examples_unmarshal_and_metadata\"\u003e\u003c/a\u003e\n\nIt may happen that your CSV input will not always have the same header. In addition\nto your base fields you may get extra metadata that you would still like to store.\n[Decoder](https://godoc.org/github.com/jszwec/csvutil#Decoder) provides \n[Unused](https://godoc.org/github.com/jszwec/csvutil#Decoder.Unused) method, which after each call to \n[Decode](https://godoc.org/github.com/jszwec/csvutil#Decoder.Decode) can report which header indexes \nwere not used during decoding. Based on that, it is possible to handle and store all these extra values.\n\n```go\n\ttype User struct {\n\t\tName      string            `csv:\"name\"`\n\t\tCity      string            `csv:\"city\"`\n\t\tAge       int               `csv:\"age\"`\n\t\tOtherData map[string]string `csv:\"-\"`\n\t}\n\n\tcsvReader := csv.NewReader(strings.NewReader(`\nname,age,city,zip\nalice,25,la,90005\nbob,30,ny,10005`))\n\n\tdec, err := csvutil.NewDecoder(csvReader)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\theader := dec.Header()\n\tvar users []User\n\tfor {\n\t\tu := User{OtherData: make(map[string]string)}\n\n\t\tif err := dec.Decode(\u0026u); err == io.EOF {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\n\t\tfor _, i := range dec.Unused() {\n\t\t\tu.OtherData[header[i]] = dec.Record()[i]\n\t\t}\n\t\tusers = append(users, u)\n\t}\n\n\tfmt.Println(users)\n\n\t// Output:\n\t// [{alice la 25 map[zip:90005]} {bob ny 30 map[zip:10005]}]\n```\n\n### But my CSV file has no header... \u003ca name=\"examples_but_my_csv_has_no_header\"\u003e\u003c/a\u003e\n\nSome CSV files have no header, but if you know how it should look like, it is\npossible to define a struct and generate it. All that is left to do, is to pass\nit to a decoder.\n\n```go\n\ttype User struct {\n\t\tID   int\n\t\tName string\n\t\tAge  int `csv:\",omitempty\"`\n\t\tCity string\n\t}\n\n\tcsvReader := csv.NewReader(strings.NewReader(`\n1,John,27,la\n2,Bob,,ny`))\n\n\t// in real application this should be done once in init function.\n\tuserHeader, err := csvutil.Header(User{}, \"csv\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tdec, err := csvutil.NewDecoder(csvReader, userHeader...)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tvar users []User\n\tfor {\n\t\tvar u User\n\t\tif err := dec.Decode(\u0026u); err == io.EOF {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tusers = append(users, u)\n\t}\n\n\tfmt.Printf(\"%+v\", users)\n\n\t// Output:\n\t// [{ID:1 Name:John Age:27 City:la} {ID:2 Name:Bob Age:0 City:ny}]\n```\n\n### Decoder.Map - data normalization \u003ca name=\"examples_decoder_map\"\u003e\u003c/a\u003e\n\nThe Decoder's [Map](https://godoc.org/github.com/jszwec/csvutil#Decoder.Map) function is a powerful tool that can help clean up or normalize\nthe incoming data before the actual decoding takes place.\n\nLets say we want to decode some floats and the csv input contains some NaN values, but these values are represented by the 'n/a' string. An attempt to decode 'n/a' into float will end up with error, because strconv.ParseFloat expects 'NaN'. Knowing that, we can implement a Map function that will normalize our 'n/a' string and turn it to 'NaN' only for float types.\n\n```go\n\tdec, err := csvutil.NewDecoder(r)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tdec.Map = func(field, column string, v any) string {\n\t\tif _, ok := v.(float64); ok \u0026\u0026 field == \"n/a\" {\n\t\t\treturn \"NaN\"\n\t\t}\n\t\treturn field\n\t}\n```\n\nNow our float64 fields will be decoded properly into NaN. What about float32, float type aliases and other NaN formats? Look at the full example [here](https://gist.github.com/jszwec/2bb94f8f3612e0162eb16003701f727e).\n\n### Different separator/delimiter \u003ca name=\"examples_different_separator\"\u003e\u003c/a\u003e\n\nSome files may use different value separators, for example TSV files would use `\\t`. The following examples show how to set up a Decoder and Encoder for such use case.\n\n#### Decoder:\n```go\n\tcsvReader := csv.NewReader(r)\n\tcsvReader.Comma = '\\t'\n\n\tdec, err := csvutil.NewDecoder(csvReader)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tvar users []User\n\tfor {\n\t\tvar u User\n\t\tif err := dec.Decode(\u0026u); err == io.EOF {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tusers = append(users, u)\n\t}\n\n```\n\n#### Encoder:\n```go\n\tvar buf bytes.Buffer\n\n\tw := csv.NewWriter(\u0026buf)\n        w.Comma = '\\t'\n\tenc := csvutil.NewEncoder(w)\n\n\tfor _, u := range users {\n\t\tif err := enc.Encode(u); err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n        }\n\n\tw.Flush()\n\tif err := w.Error(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n```\n\n### Custom Types and Overrides \u003ca name=\"examples_custom_types\"\u003e\u003c/a\u003e\n\nThere are multiple ways to customize or override your type's behavior.\n\n1. a type implements [csvutil.Marshaler](https://pkg.go.dev/github.com/jszwec/csvutil#Marshaler) and/or [csvutil.Unmarshaler](https://pkg.go.dev/github.com/jszwec/csvutil#Unmarshaler)\n```go\ntype Foo int64\n\nfunc (f Foo) MarshalCSV() ([]byte, error) {\n\treturn strconv.AppendInt(nil, int64(f), 16), nil\n}\n\nfunc (f *Foo) UnmarshalCSV(data []byte) error {\n\ti, err := strconv.ParseInt(string(data), 16, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\t*f = Foo(i)\n\treturn nil\n}\n```\n2. a type implements [encoding.TextUnmarshaler](https://golang.org/pkg/encoding/#TextUnmarshaler) and/or [encoding.TextMarshaler](https://golang.org/pkg/encoding/#TextMarshaler)\n```go\ntype Foo int64\n\nfunc (f Foo) MarshalText() ([]byte, error) {\n\treturn strconv.AppendInt(nil, int64(f), 16), nil\n}\n\nfunc (f *Foo) UnmarshalText(data []byte) error {\n\ti, err := strconv.ParseInt(string(data), 16, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\t*f = Foo(i)\n\treturn nil\n}\n```\n3. a type is registered using [Encoder.WithMarshalers](https://pkg.go.dev/github.com/jszwec/csvutil#Encoder.WithMarshalers) and/or [Decoder.WithUnmarshalers](https://pkg.go.dev/github.com/jszwec/csvutil#Decoder.WithUnmarshalers)\n```go\ntype Foo int64\n\nenc.WithMarshalers(\n\tcsvutil.MarshalFunc(func(f Foo) ([]byte, error) {\n\t\treturn strconv.AppendInt(nil, int64(f), 16), nil\n\t}),\n)\n\ndec.WithUnmarshalers(\n\tcsvutil.UnmarshalFunc(func(data []byte, f *Foo) error {\n\t\tv, err := strconv.ParseInt(string(data), 16, 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*f = Foo(v)\n\t\treturn nil\n\t}),\n)\n```\n4. a type implements an interface that was registered using [Encoder.WithMarshalers](https://pkg.go.dev/github.com/jszwec/csvutil#Encoder.WithMarshalers) and/or [Decoder.WithUnmarshalers](https://pkg.go.dev/github.com/jszwec/csvutil#Decoder.WithUnmarshalers)\n```go\ntype Foo int64\n\nfunc (f Foo) String() string {\n\treturn strconv.FormatInt(int64(f), 16)\n}\n\nfunc (f *Foo) Scan(state fmt.ScanState, verb rune) error {\n\t// too long; look here: https://github.com/jszwec/csvutil/blob/master/example_decoder_register_test.go#L19\n}\n\nenc.WithMarshalers(\n\tcsvutil.MarshalFunc(func(s fmt.Stringer) ([]byte, error) {\n\t\treturn []byte(s.String()), nil\n\t}),\n)\n\ndec.WithUnmarshalers(\n\tcsvutil.UnmarshalFunc(func(data []byte, s fmt.Scanner) error {\n\t\t_, err := fmt.Sscan(string(data), s)\n\t\treturn err\n\t}),\n)\n```\n\nThe order of precedence for both Encoder and Decoder is:\n1. type is registered\n2. type implements an interface that was registered\n3. csvutil.{Un,M}arshaler\n4. encoding.Text{Un,M}arshaler\n\nFor more examples look [here](https://pkg.go.dev/github.com/jszwec/csvutil?readme=expanded#pkg-examples)\n\n### Custom time.Time format \u003ca name=\"examples_time_format\"\u003e\u003c/a\u003e\n\nType [time.Time](https://golang.org/pkg/time/#Time) can be used as is in the struct fields by both Decoder and Encoder\ndue to the fact that both have builtin support for [encoding.TextUnmarshaler](https://golang.org/pkg/encoding/#TextUnmarshaler) and [encoding.TextMarshaler](https://golang.org/pkg/encoding/#TextMarshaler). This means that by default\nTime has a specific format; look at [MarshalText](https://golang.org/pkg/time/#Time.MarshalText) and [UnmarshalText](https://golang.org/pkg/time/#Time.UnmarshalText). There are two ways to override it, which one you choose depends on your use case:\n\n1. Via Register func (based on encoding/json)\n```go\nconst format = \"2006/01/02 15:04:05\"\n\nmarshalTime := func(t time.Time) ([]byte, error) {\n\treturn t.AppendFormat(nil, format), nil\n}\n\nunmarshalTime := func(data []byte, t *time.Time) error {\n\ttt, err := time.Parse(format, string(data))\n\tif err != nil {\n\t\treturn err\n\t}\n\t*t = tt\n\treturn nil\n}\n\nenc := csvutil.NewEncoder(w)\nenc.Register(marshalTime)\n\ndec, err := csvutil.NewDecoder(r)\nif err != nil {\n\treturn err\n}\ndec.Register(unmarshalTime)\n```\n\n2. With custom type:\n```go\ntype Time struct {\n\ttime.Time\n}\n\nconst format = \"2006/01/02 15:04:05\"\n\nfunc (t Time) MarshalCSV() ([]byte, error) {\n\tvar b [len(format)]byte\n\treturn t.AppendFormat(b[:0], format), nil\n}\n\nfunc (t *Time) UnmarshalCSV(data []byte) error {\n\ttt, err := time.Parse(format, string(data))\n\tif err != nil {\n\t\treturn err\n\t}\n\t*t = Time{Time: tt}\n\treturn nil\n}\n```\n\n### Custom struct tags \u003ca name=\"examples_struct_tags\"\u003e\u003c/a\u003e\n\nLike in other Go encoding packages struct field tags can be used to set\ncustom names or options. By default encoders and decoders are looking at `csv` tag.\nHowever, this can be overriden by manually setting the Tag field.\n\n```go\n\ttype Foo struct {\n\t\tBar int `custom:\"bar\"`\n\t}\n```\n\n```go\n\tdec, err := csvutil.NewDecoder(r)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdec.Tag = \"custom\"\n```\n\n```go\n\tenc := csvutil.NewEncoder(w)\n\tenc.Tag = \"custom\"\n```\n\n### Slice and Map fields \u003ca name=\"examples_slice_and_map_field\"\u003e\u003c/a\u003e\n\nThere is no default encoding/decoding support for slice and map fields because there is no CSV spec for such values.\nIn such case, it is recommended to create a custom type alias and implement Marshaler and Unmarshaler interfaces.\nPlease note that slice and map aliases behave differently than aliases of other types - there is no need for type casting.\n\n```go\n\ttype Strings []string\n\n\tfunc (s Strings) MarshalCSV() ([]byte, error) {\n\t\treturn []byte(strings.Join(s, \",\")), nil // strings.Join takes []string but it will also accept Strings\n\t}\n\n\ttype StringMap map[string]string\n\n\tfunc (sm StringMap) MarshalCSV() ([]byte, error) {\n\t\treturn []byte(fmt.Sprint(sm)), nil\n\t}\n\n\tfunc main() {\n\t\tb, err := csvutil.Marshal([]struct {\n\t\t\tStrings Strings   `csv:\"strings\"`\n\t\t\tMap     StringMap `csv:\"map\"`\n\t\t}{\n\t\t\t{[]string{\"a\", \"b\"}, map[string]string{\"a\": \"1\"}}, // no type casting is required for slice and map aliases\n\t\t\t{Strings{\"c\", \"d\"}, StringMap{\"b\": \"1\"}},\n\t\t})\n\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\n\t\tfmt.Printf(\"%s\\n\", b)\n\n\t\t// Output:\n\t\t// strings,map\n\t\t// \"a,b\",map[a:1]\n\t\t// \"c,d\",map[b:1]\n\t}\n```\n\n### Nested/Embedded structs \u003ca name=\"examples_nested_structs\"\u003e\u003c/a\u003e\n\nBoth Encoder and Decoder support nested or embedded structs.\n\nPlayground: https://play.golang.org/p/ZySjdVkovbf\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/jszwec/csvutil\"\n)\n\ntype Address struct {\n\tStreet string `csv:\"street\"`\n\tCity   string `csv:\"city\"`\n}\n\ntype User struct {\n\tName string `csv:\"name\"`\n\tAddress\n}\n\nfunc main() {\n\tusers := []User{\n\t\t{\n\t\t\tName: \"John\",\n\t\t\tAddress: Address{\n\t\t\t\tStreet: \"Boylston\",\n\t\t\t\tCity:   \"Boston\",\n\t\t\t},\n\t\t},\n\t}\n\n\tb, err := csvutil.Marshal(users)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"%s\\n\", b)\n\n\tvar out []User\n\tif err := csvutil.Unmarshal(b, \u0026out); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", out)\n\n\t// Output:\n\t//\n\t// name,street,city\n\t// John,Boylston,Boston\n\t//\n\t// [{Name:John Address:{Street:Boylston City:Boston}}]\n}\n```\n\n### Inline tag \u003ca name=\"examples_inlined_structs\"\u003e\u003c/a\u003e\n\nFields with inline tag behave similarly to embedded struct fields. However,\nit gives a possibility to specify the prefix for all underlying fields. This\ncan be useful when one structure can define multiple CSV columns because they \nare different from each other only by a certain prefix. Look at the example below.\n\nPlayground: https://play.golang.org/p/jyEzeskSnj7\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/jszwec/csvutil\"\n)\n\nfunc main() {\n\ttype Address struct {\n\t\tStreet string `csv:\"street\"`\n\t\tCity   string `csv:\"city\"`\n\t}\n\n\ttype User struct {\n\t\tName        string  `csv:\"name\"`\n\t\tAddress     Address `csv:\",inline\"`\n\t\tHomeAddress Address `csv:\"home_address_,inline\"`\n\t\tWorkAddress Address `csv:\"work_address_,inline\"`\n\t\tAge         int     `csv:\"age,omitempty\"`\n\t}\n\n\tusers := []User{\n\t\t{\n\t\t\tName:        \"John\",\n\t\t\tAddress:     Address{\"Washington\", \"Boston\"},\n\t\t\tHomeAddress: Address{\"Boylston\", \"Boston\"},\n\t\t\tWorkAddress: Address{\"River St\", \"Cambridge\"},\n\t\t\tAge:         26,\n\t\t},\n\t}\n\n\tb, err := csvutil.Marshal(users)\n\tif err != nil {\n\t\tfmt.Println(\"error:\", err)\n\t}\n\n\tfmt.Printf(\"%s\\n\", b)\n\n\t// Output:\n\t// name,street,city,home_address_street,home_address_city,work_address_street,work_address_city,age\n\t// John,Washington,Boston,Boylston,Boston,River St,Cambridge,26\n}\n```\n\nPerformance\n------------\n\ncsvutil provides the best encoding and decoding performance with small memory usage.\n\n### Unmarshal \u003ca name=\"performance_unmarshal\"\u003e\u003c/a\u003e\n\n[benchmark code](https://gist.github.com/jszwec/e8515e741190454fa3494bcd3e1f100f)\n\n#### csvutil:\n```\nBenchmarkUnmarshal/csvutil.Unmarshal/1_record-12         \t  280696\t      4516 ns/op\t    7332 B/op\t      26 allocs/op\nBenchmarkUnmarshal/csvutil.Unmarshal/10_records-12       \t   95750\t     11517 ns/op\t    8356 B/op\t      35 allocs/op\nBenchmarkUnmarshal/csvutil.Unmarshal/100_records-12      \t   14997\t     83146 ns/op\t   18532 B/op\t     125 allocs/op\nBenchmarkUnmarshal/csvutil.Unmarshal/1000_records-12     \t    1485\t    750143 ns/op\t  121094 B/op\t    1025 allocs/op\nBenchmarkUnmarshal/csvutil.Unmarshal/10000_records-12    \t     154\t   7587205 ns/op\t 1136662 B/op\t   10025 allocs/op\nBenchmarkUnmarshal/csvutil.Unmarshal/100000_records-12   \t      14\t  76126616 ns/op\t11808744 B/op\t  100025 allocs/op\n```\n\n#### gocsv:\n```\nBenchmarkUnmarshal/gocsv.Unmarshal/1_record-12           \t  141330\t      7499 ns/op\t    7795 B/op\t      97 allocs/op\nBenchmarkUnmarshal/gocsv.Unmarshal/10_records-12         \t   54252\t     21664 ns/op\t   13891 B/op\t     307 allocs/op\nBenchmarkUnmarshal/gocsv.Unmarshal/100_records-12        \t    6920\t    159662 ns/op\t   72644 B/op\t    2380 allocs/op\nBenchmarkUnmarshal/gocsv.Unmarshal/1000_records-12       \t     752\t   1556083 ns/op\t  650248 B/op\t   23083 allocs/op\nBenchmarkUnmarshal/gocsv.Unmarshal/10000_records-12      \t      72\t  17086623 ns/op\t 7017469 B/op\t  230092 allocs/op\nBenchmarkUnmarshal/gocsv.Unmarshal/100000_records-12     \t       7\t 163610749 ns/op\t75004923 B/op\t 2300105 allocs/op\n```\n\n#### easycsv:\n```\nBenchmarkUnmarshal/easycsv.ReadAll/1_record-12           \t  101527\t     10662 ns/op\t    8855 B/op\t      81 allocs/op\nBenchmarkUnmarshal/easycsv.ReadAll/10_records-12         \t   23325\t     51437 ns/op\t   24072 B/op\t     391 allocs/op\nBenchmarkUnmarshal/easycsv.ReadAll/100_records-12        \t    2402\t    447296 ns/op\t  170538 B/op\t    3454 allocs/op\nBenchmarkUnmarshal/easycsv.ReadAll/1000_records-12       \t     272\t   4370854 ns/op\t 1595683 B/op\t   34057 allocs/op\nBenchmarkUnmarshal/easycsv.ReadAll/10000_records-12      \t      24\t  47502457 ns/op\t18861808 B/op\t  340068 allocs/op\nBenchmarkUnmarshal/easycsv.ReadAll/100000_records-12     \t       3\t 468974170 ns/op\t189427066 B/op\t 3400082 allocs/op\n```\n\n### Marshal \u003ca name=\"performance_marshal\"\u003e\u003c/a\u003e\n\n[benchmark code](https://gist.github.com/jszwec/31980321e1852ebb5615a44ccf374f17)\n\n#### csvutil:\n```\nBenchmarkMarshal/csvutil.Marshal/1_record-12         \t  279558\t      4390 ns/op\t    9952 B/op\t      12 allocs/op\nBenchmarkMarshal/csvutil.Marshal/10_records-12       \t   82478\t     15608 ns/op\t   10800 B/op\t      21 allocs/op\nBenchmarkMarshal/csvutil.Marshal/100_records-12      \t   10275\t    117288 ns/op\t   28208 B/op\t     112 allocs/op\nBenchmarkMarshal/csvutil.Marshal/1000_records-12     \t    1075\t   1147473 ns/op\t  168508 B/op\t    1014 allocs/op\nBenchmarkMarshal/csvutil.Marshal/10000_records-12    \t     100\t  11985382 ns/op\t 1525973 B/op\t   10017 allocs/op\nBenchmarkMarshal/csvutil.Marshal/100000_records-12   \t       9\t 113640813 ns/op\t22455873 B/op\t  100021 allocs/op\n```\n\n#### gocsv:\n```\nBenchmarkMarshal/gocsv.Marshal/1_record-12           \t  203052\t      6077 ns/op\t    5914 B/op\t      81 allocs/op\nBenchmarkMarshal/gocsv.Marshal/10_records-12         \t   50132\t     24585 ns/op\t    9284 B/op\t     360 allocs/op\nBenchmarkMarshal/gocsv.Marshal/100_records-12        \t    5480\t    212008 ns/op\t   51916 B/op\t    3151 allocs/op\nBenchmarkMarshal/gocsv.Marshal/1000_records-12       \t     514\t   2053919 ns/op\t  444506 B/op\t   31053 allocs/op\nBenchmarkMarshal/gocsv.Marshal/10000_records-12      \t      52\t  21066666 ns/op\t 4332377 B/op\t  310064 allocs/op\nBenchmarkMarshal/gocsv.Marshal/100000_records-12     \t       5\t 207408929 ns/op\t51169419 B/op\t 3100077 allocs/op\n```\n","funding_links":[],"categories":["Go","序列化","Serialization","Repositories","安全领域相关库","Relational Databases"],"sub_categories":["HTTP客户端","HTTP Clients","查询语","Advanced Console UIs","交流"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjszwec%2Fcsvutil","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjszwec%2Fcsvutil","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjszwec%2Fcsvutil/lists"}