diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2018-05-05 13:33:19 +0800 |
---|---|---|
committer | Lunny Xiao <xiaolunwen@gmail.com> | 2018-05-05 13:33:19 +0800 |
commit | bc451a78de32ccde2a4f6d0851ea6e41e45408f1 (patch) | |
tree | e1c8723b1e6c61b76379018b7843acc3c9610e36 | |
parent | 934ecec1a96bfd5a1ecf3f1f373332504971c5fd (diff) |
add iterator method for rows
-rw-r--r-- | rows.go | 96 | ||||
-rw-r--r-- | rows_test.go | 42 |
2 files changed, 138 insertions, 0 deletions
@@ -3,6 +3,8 @@ package excelize import ( "bytes" "encoding/xml" + "fmt" + "io" "math" "strconv" "strings" @@ -67,6 +69,100 @@ func (f *File) GetRows(sheet string) [][]string { return rows } +// Rows defines an iterator to a sheet +type Rows struct { + decoder *xml.Decoder + token xml.Token + err error + f *File +} + +// Next will return true if find the next row element. +func (rows *Rows) Next() bool { + for { + rows.token, rows.err = rows.decoder.Token() + if rows.err == io.EOF { + rows.err = nil + } + if rows.token == nil { + return false + } + + switch startElement := rows.token.(type) { + case xml.StartElement: + inElement := startElement.Name.Local + if inElement == "row" { + return true + } + } + } +} + +// Error will return the error when the find next row element +func (rows *Rows) Error() error { + return rows.err +} + +// Columns return the current row's column values +func (rows *Rows) Columns() []string { + if rows.token == nil { + return []string{} + } + + startElement := rows.token.(xml.StartElement) + r := xlsxRow{} + rows.decoder.DecodeElement(&r, &startElement) + + d := rows.f.sharedStringsReader() + row := make([]string, len(r.C), len(r.C)) + for _, colCell := range r.C { + c := TitleToNumber(strings.Map(letterOnlyMapF, colCell.R)) + val, _ := colCell.getValueFrom(rows.f, d) + row[c] = val + } + return row +} + +// ErrSheetNotExist defines an error of sheet is not exist +type ErrSheetNotExist struct { + SheetName string +} + +func (err ErrSheetNotExist) Error() string { + return fmt.Sprintf("Sheet %s is not exist", string(err.SheetName)) +} + +// Rows return a rows iterator. For example: +// +// rows, err := xlsx.GetRows("Sheet1") +// +// for rows.Next() { +// for _, colCell := range rows.Columns() { +// fmt.Print(colCell, "\t") +// } +// fmt.Println() +// } +// +func (f *File) Rows(sheet string) (*Rows, error) { + xlsx := f.workSheetReader(sheet) + name, ok := f.sheetMap[trimSheetName(sheet)] + if !ok { + return nil, ErrSheetNotExist{sheet} + } + if xlsx != nil { + output, err := xml.Marshal(f.Sheet[name]) + if err != nil { + return nil, err + } + f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpace(string(output))) + } + + return &Rows{ + f: f, + decoder: xml.NewDecoder(strings.NewReader(f.readXML(name))), + }, nil +} + // getTotalRowsCols provides a function to get total columns and rows in a // worksheet. func (f *File) getTotalRowsCols(name string) (int, int) { diff --git a/rows_test.go b/rows_test.go new file mode 100644 index 0000000..eadf077 --- /dev/null +++ b/rows_test.go @@ -0,0 +1,42 @@ +package excelize + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func trimSliceSpace(s []string) []string { + for { + if len(s) > 0 && s[len(s)-1] == "" { + s = s[:len(s)-1] + } else { + break + } + } + return s +} + +func TestRows(t *testing.T) { + xlsx, err := OpenFile("./test/Book1.xlsx") + assert.NoError(t, err) + + rows, err := xlsx.Rows("Sheet2") + assert.NoError(t, err) + + rowStrs := make([][]string, 0) + var i = 0 + for rows.Next() { + i++ + columns := rows.Columns() + //fmt.Println(i, columns) + rowStrs = append(rowStrs, columns) + } + assert.NoError(t, rows.Error()) + + dstRows := xlsx.GetRows("Sheet2") + assert.EqualValues(t, len(dstRows), len(rowStrs)) + for i := 0; i < len(rowStrs); i++ { + assert.EqualValues(t, trimSliceSpace(dstRows[i]), trimSliceSpace(rowStrs[i])) + } +} |