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 }