Init
This commit is contained in:
parent
649144b0ac
commit
4e501a6ab7
28
README.md
28
README.md
@ -1,2 +1,30 @@
|
|||||||
# go-csv-table
|
# go-csv-table
|
||||||
|
|
||||||
|
This module reads a RTF and let you get the information per field or column.
|
||||||
|
|
||||||
|
**PLEASE NOTE**: the entire CSV fill be read to the memory at once!
|
||||||
|
|
||||||
|
## Example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
gct := GoCSVtable{}
|
||||||
|
gct.OpenCSV("file.csv")
|
||||||
|
|
||||||
|
for {
|
||||||
|
name, err := gct.Read("Name")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
surname, err := gct.Read("Surname")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err := gct.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
// File is over...
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3
go.mod
Normal file
3
go.mod
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module gitea.brunofontes.net/BFT/go-csv-table
|
||||||
|
|
||||||
|
go 1.24.1
|
129
gocsvtable.go
Normal file
129
gocsvtable.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package gocsvtable
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GoCSVtable struct {
|
||||||
|
file *csv.Reader
|
||||||
|
rows int
|
||||||
|
data [][]string
|
||||||
|
headers map[string]int
|
||||||
|
|
||||||
|
Separator rune
|
||||||
|
Comment rune
|
||||||
|
|
||||||
|
lastRow int
|
||||||
|
|
||||||
|
// Row is the active CSV row, the data will start at 1
|
||||||
|
Row int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gct *GoCSVtable) OpenCSV(filename string) error {
|
||||||
|
osfile, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Not possible to os.Open file: %s\n", err)
|
||||||
|
}
|
||||||
|
defer osfile.Close()
|
||||||
|
|
||||||
|
// The default rune is 0
|
||||||
|
if gct.Separator != 0 {
|
||||||
|
gct.file.Comma = gct.Separator
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default rune is 0
|
||||||
|
if gct.Comment != 0 {
|
||||||
|
gct.file.Comment = gct.Comment
|
||||||
|
}
|
||||||
|
|
||||||
|
gct.file = csv.NewReader(osfile)
|
||||||
|
gct.data, err = gct.file.ReadAll()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Not possible to csv.ReadAll file: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gct.setHeaders()
|
||||||
|
|
||||||
|
// First data row is 1
|
||||||
|
gct.Row = 1
|
||||||
|
|
||||||
|
// Last row number
|
||||||
|
gct.lastRow = len(gct.data) - 1
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setHeaders will associate the headers names with the correct column number
|
||||||
|
func (gct *GoCSVtable) setHeaders() {
|
||||||
|
header := gct.data[0]
|
||||||
|
columnsNumber := len(header)
|
||||||
|
|
||||||
|
// Initiate Headers
|
||||||
|
gct.headers = make(map[string]int)
|
||||||
|
|
||||||
|
for field := 0; field < columnsNumber; field++ {
|
||||||
|
name := header[field]
|
||||||
|
gct.headers[name] = field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeader return the col number from a field string
|
||||||
|
func (gct *GoCSVtable) GetHeader(field string) (int, error) {
|
||||||
|
col, ok := gct.headers[field]
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("Field '%s' does not exist", field)
|
||||||
|
}
|
||||||
|
|
||||||
|
return col, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next will jump to the next CSV row. If file is over, will return an io.EOF
|
||||||
|
func (gct *GoCSVtable) Next() error {
|
||||||
|
|
||||||
|
if gct.Row >= gct.lastRow {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
gct.Row++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNumberOfRows will return the number of data rows, not considering the
|
||||||
|
// header
|
||||||
|
func (gct *GoCSVtable) GetNumberOfRows() int {
|
||||||
|
return gct.lastRow
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read return a specific field from active row
|
||||||
|
func (gct *GoCSVtable) Read(field string) (string, error) {
|
||||||
|
col, err := gct.GetHeader(field)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return gct.data[gct.Row][col], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read and return the entire row
|
||||||
|
func (gct *GoCSVtable) ReadRow(row int) ([]string, error) {
|
||||||
|
if row > gct.lastRow {
|
||||||
|
return []string{}, fmt.Errorf("Not possible to read row %v. Data has max of %v rows.\n", row, gct.lastRow)
|
||||||
|
}
|
||||||
|
return gct.data[row], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadCol will return a slice with all row values from a single column (field)
|
||||||
|
func (gct *GoCSVtable) ReadCol(field string) ([]string, error) {
|
||||||
|
data := []string{}
|
||||||
|
fieldNumber, err := gct.GetHeader(field)
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for row := 1; row <= gct.lastRow; row++ {
|
||||||
|
data = append(data, gct.data[row][fieldNumber])
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
149
gocsvtable_test.go
Normal file
149
gocsvtable_test.go
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
package gocsvtable
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const testFile = "test.csv"
|
||||||
|
|
||||||
|
func TestOpenCSV(t *testing.T) {
|
||||||
|
gct := GoCSVtable{}
|
||||||
|
err := gct.OpenCSV(testFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("\nError opening csv file:\n - %#v\n", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetHeader(t *testing.T) {
|
||||||
|
const letters = 0
|
||||||
|
const numbers = 1
|
||||||
|
|
||||||
|
gct := GoCSVtable{}
|
||||||
|
gct.OpenCSV(testFile)
|
||||||
|
|
||||||
|
_, err := gct.GetHeader("Non Existent Field")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Getting header of non existent field should raise error.")
|
||||||
|
}
|
||||||
|
|
||||||
|
gotLettersCol, _ := gct.GetHeader("Letters")
|
||||||
|
if gotLettersCol != letters {
|
||||||
|
t.Errorf("Error reading correct 'Letters' column. Expected: '%v'. Got: '%v'.\n", letters, gotLettersCol)
|
||||||
|
}
|
||||||
|
|
||||||
|
gotNumbersCol, _ := gct.GetHeader("Numbers")
|
||||||
|
if gotNumbersCol != numbers {
|
||||||
|
t.Errorf("Error reading correct 'Numbers' column. Expected: '%v'. Got: '%v'.\n", numbers, gotNumbersCol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNext(t *testing.T) {
|
||||||
|
gct := GoCSVtable{}
|
||||||
|
gct.OpenCSV(testFile)
|
||||||
|
|
||||||
|
for row := 2; row < 5; row++ {
|
||||||
|
err := gct.Next()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error passing to next row. CSV file have 5 rows, not %v", row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to go to different row, it should receive io.EOF
|
||||||
|
err := gct.Next()
|
||||||
|
if err != io.EOF {
|
||||||
|
t.Errorf("Error on Next(), row should be inexistent. Expected: '%v'; Got: %v", io.EOF, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetNumberOfRows(t *testing.T) {
|
||||||
|
gct := GoCSVtable{}
|
||||||
|
gct.OpenCSV(testFile)
|
||||||
|
|
||||||
|
expected := 4 // 1 Header + 4 Data
|
||||||
|
if gct.GetNumberOfRows() != expected {
|
||||||
|
t.Errorf("Error getting number of rows in file. Expected: '%v'. Got: '%v'", expected, gct.GetNumberOfRows())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRead(t *testing.T) {
|
||||||
|
gct := GoCSVtable{}
|
||||||
|
gct.OpenCSV(testFile)
|
||||||
|
|
||||||
|
row := 2
|
||||||
|
expected := "b"
|
||||||
|
|
||||||
|
gct.Row = row
|
||||||
|
got, err := gct.Read("Letters")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v", err.Error())
|
||||||
|
}
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("Error getting correct field on row '%v'. Expected: '%v'. Got: '%v'", row, expected, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = "2"
|
||||||
|
got, err = gct.Read("Numbers")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v", err.Error())
|
||||||
|
}
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("Error getting correct field on row '%v'. Expected: '%v'. Got: '%v'", row, expected, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadRow(t *testing.T) {
|
||||||
|
gct := GoCSVtable{}
|
||||||
|
gct.OpenCSV(testFile)
|
||||||
|
|
||||||
|
row := 3
|
||||||
|
|
||||||
|
gct.Row = row
|
||||||
|
got, err := gct.ReadRow(row)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error getting correct row: '%v'", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
col, err := gct.GetHeader("Letters")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v", err.Error())
|
||||||
|
}
|
||||||
|
gotLetter := got[col]
|
||||||
|
expected := "c"
|
||||||
|
if gotLetter != expected {
|
||||||
|
t.Errorf("Error getting correct row. Expected: '[%v %v]'. Got: '%v'", expected, row, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
col, err = gct.GetHeader("Numbers")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v", err.Error())
|
||||||
|
}
|
||||||
|
gotNumber := got[col]
|
||||||
|
expected = "3"
|
||||||
|
if gotNumber != expected {
|
||||||
|
t.Errorf("Error getting correct row. Expected: '[%v %v]'. Got: '%v'", expected, row, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
row = 7
|
||||||
|
got, err = gct.ReadRow(row)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Getting inexistent row should raise error. Value: '%v'. Error: '%v'", got, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadCol(t *testing.T) {
|
||||||
|
gct := GoCSVtable{}
|
||||||
|
gct.OpenCSV(testFile)
|
||||||
|
|
||||||
|
expected := []string{"a", "b", "c", "d"}
|
||||||
|
col := "Letters"
|
||||||
|
got, err := gct.ReadCol(col)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expected, got) {
|
||||||
|
t.Errorf("Error getting column. Expected: '%v'. Got: '%v'", expected, got)
|
||||||
|
}
|
||||||
|
}
|
88
tags
Normal file
88
tags
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
!_TAG_EXTRA_DESCRIPTION anonymous /Include tags for non-named objects like lambda/
|
||||||
|
!_TAG_EXTRA_DESCRIPTION fileScope /Include tags of file scope/
|
||||||
|
!_TAG_EXTRA_DESCRIPTION pseudo /Include pseudo tags/
|
||||||
|
!_TAG_EXTRA_DESCRIPTION subparser /Include tags generated by subparsers/
|
||||||
|
!_TAG_FIELD_DESCRIPTION epoch /the last modified time of the input file (only for F\/file kind tag)/
|
||||||
|
!_TAG_FIELD_DESCRIPTION file /File-restricted scoping/
|
||||||
|
!_TAG_FIELD_DESCRIPTION input /input file/
|
||||||
|
!_TAG_FIELD_DESCRIPTION name /tag name/
|
||||||
|
!_TAG_FIELD_DESCRIPTION pattern /pattern/
|
||||||
|
!_TAG_FIELD_DESCRIPTION typeref /Type and name of a variable or typedef/
|
||||||
|
!_TAG_FIELD_DESCRIPTION!Go package /the real package specified by the package name/
|
||||||
|
!_TAG_FIELD_DESCRIPTION!Go packageName /the name for referring the package/
|
||||||
|
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
|
||||||
|
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
|
||||||
|
!_TAG_KIND_DESCRIPTION!DTD E,entity /entities/
|
||||||
|
!_TAG_KIND_DESCRIPTION!DTD a,attribute /attributes/
|
||||||
|
!_TAG_KIND_DESCRIPTION!DTD e,element /elements/
|
||||||
|
!_TAG_KIND_DESCRIPTION!DTD n,notation /notations/
|
||||||
|
!_TAG_KIND_DESCRIPTION!DTD p,parameterEntity /parameter entities/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Go M,anonMember /struct anonymous members/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Go P,packageName /name for specifying imported package/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Go Y,unknown /unknown/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Go a,talias /type aliases/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Go c,const /constants/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Go f,func /functions/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Go i,interface /interfaces/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Go m,member /struct members/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Go n,methodSpec /interface method specification/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Go p,package /packages/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Go s,struct /structs/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Go t,type /types/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Go v,var /variables/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Markdown S,subsection /level 2 sections/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Markdown T,l4subsection /level 4 sections/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Markdown c,chapter /chapters/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Markdown h,hashtag /hashtags/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Markdown n,footnote /footnotes/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Markdown s,section /sections/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Markdown t,subsubsection /level 3 sections/
|
||||||
|
!_TAG_KIND_DESCRIPTION!Markdown u,l5subsection /level 5 sections/
|
||||||
|
!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/
|
||||||
|
!_TAG_OUTPUT_FILESEP slash /slash or backslash/
|
||||||
|
!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/
|
||||||
|
!_TAG_OUTPUT_VERSION 0.0 /current.age/
|
||||||
|
!_TAG_PARSER_VERSION!DTD 0.0 /current.age/
|
||||||
|
!_TAG_PARSER_VERSION!Go 0.0 /current.age/
|
||||||
|
!_TAG_PARSER_VERSION!Markdown 1.1 /current.age/
|
||||||
|
!_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/
|
||||||
|
!_TAG_PROC_CWD /run/media/bruno/Multimedia/MyDocuments/Development/BrunoFontes/GO/go-csv-table/ //
|
||||||
|
!_TAG_PROGRAM_AUTHOR Universal Ctags Team //
|
||||||
|
!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/
|
||||||
|
!_TAG_PROGRAM_URL https://ctags.io/ /official site/
|
||||||
|
!_TAG_PROGRAM_VERSION 6.1.0 /v6.1.0/
|
||||||
|
!_TAG_ROLE_DESCRIPTION!DTD!element attOwner /attributes owner/
|
||||||
|
!_TAG_ROLE_DESCRIPTION!DTD!parameterEntity condition /conditions/
|
||||||
|
!_TAG_ROLE_DESCRIPTION!DTD!parameterEntity elementName /element names/
|
||||||
|
!_TAG_ROLE_DESCRIPTION!DTD!parameterEntity partOfAttDef /part of attribute definition/
|
||||||
|
!_TAG_ROLE_DESCRIPTION!Go!package imported /imported package/
|
||||||
|
!_TAG_ROLE_DESCRIPTION!Go!unknown receiverType /receiver type/
|
||||||
|
Comment gocsvtable.go /^ Comment rune$/;" m struct:gocsvtable.GoCSVtable typeref:typename:rune
|
||||||
|
Example: README.md /^## Example:$/;" s chapter:go-csv-table
|
||||||
|
GetHeader gocsvtable.go /^func (gct *GoCSVtable) GetHeader(field string) (int, error) {$/;" f struct:gocsvtable.GoCSVtable typeref:typename:(int, error)
|
||||||
|
GetNumberOfRows gocsvtable.go /^func (gct *GoCSVtable) GetNumberOfRows() int {$/;" f struct:gocsvtable.GoCSVtable typeref:typename:int
|
||||||
|
GoCSVtable gocsvtable.go /^type GoCSVtable struct {$/;" s package:gocsvtable
|
||||||
|
Next gocsvtable.go /^func (gct *GoCSVtable) Next() error {$/;" f struct:gocsvtable.GoCSVtable typeref:typename:error
|
||||||
|
OpenCSV gocsvtable.go /^func (gct *GoCSVtable) OpenCSV(filename string) error {$/;" f struct:gocsvtable.GoCSVtable typeref:typename:error
|
||||||
|
Read gocsvtable.go /^func (gct *GoCSVtable) Read(field string) (string, error) {$/;" f struct:gocsvtable.GoCSVtable typeref:typename:(string, error)
|
||||||
|
ReadCol gocsvtable.go /^func (gct *GoCSVtable) ReadCol(field string) ([]string, error) {$/;" f struct:gocsvtable.GoCSVtable typeref:typename:([]string, error)
|
||||||
|
ReadRow gocsvtable.go /^func (gct *GoCSVtable) ReadRow(row int) ([]string, error) {$/;" f struct:gocsvtable.GoCSVtable typeref:typename:([]string, error)
|
||||||
|
Row gocsvtable.go /^ Row int$/;" m struct:gocsvtable.GoCSVtable typeref:typename:int
|
||||||
|
Separator gocsvtable.go /^ Separator rune$/;" m struct:gocsvtable.GoCSVtable typeref:typename:rune
|
||||||
|
TestGetHeader gocsvtable_test.go /^func TestGetHeader(t *testing.T) {$/;" f package:gocsvtable
|
||||||
|
TestGetNumberOfRows gocsvtable_test.go /^func TestGetNumberOfRows(t *testing.T) {$/;" f package:gocsvtable
|
||||||
|
TestNext gocsvtable_test.go /^func TestNext(t *testing.T) {$/;" f package:gocsvtable
|
||||||
|
TestOpenCSV gocsvtable_test.go /^func TestOpenCSV(t *testing.T) {$/;" f package:gocsvtable
|
||||||
|
TestRead gocsvtable_test.go /^func TestRead(t *testing.T) {$/;" f package:gocsvtable
|
||||||
|
TestReadCol gocsvtable_test.go /^func TestReadCol(t *testing.T) {$/;" f package:gocsvtable
|
||||||
|
TestReadRow gocsvtable_test.go /^func TestReadRow(t *testing.T) {$/;" f package:gocsvtable
|
||||||
|
data gocsvtable.go /^ data [][]string$/;" m struct:gocsvtable.GoCSVtable typeref:typename:[][]string
|
||||||
|
file gocsvtable.go /^ file *csv.Reader$/;" m struct:gocsvtable.GoCSVtable typeref:typename:*csv.Reader
|
||||||
|
go-csv-table README.md /^# go-csv-table$/;" c
|
||||||
|
gocsvtable gocsvtable.go /^package gocsvtable$/;" p
|
||||||
|
gocsvtable gocsvtable_test.go /^package gocsvtable$/;" p
|
||||||
|
headers gocsvtable.go /^ headers map[string]int$/;" m struct:gocsvtable.GoCSVtable typeref:typename:map[string]int
|
||||||
|
lastRow gocsvtable.go /^ lastRow int$/;" m struct:gocsvtable.GoCSVtable typeref:typename:int
|
||||||
|
rows gocsvtable.go /^ rows int$/;" m struct:gocsvtable.GoCSVtable typeref:typename:int
|
||||||
|
setHeaders gocsvtable.go /^func (gct *GoCSVtable) setHeaders() {$/;" f struct:gocsvtable.GoCSVtable
|
||||||
|
testFile gocsvtable_test.go /^const testFile = "test.csv"$/;" c package:gocsvtable
|
Loading…
Reference in New Issue
Block a user