diff --git a/rows.go b/rows.go index 3079d5a4e83dbb7d400ed5b6fbd2a7af1d7060cf..cb0e31fde2a10c95cfa99fe4b8466f0fa58f87db 100644 --- a/rows.go +++ b/rows.go @@ -10,10 +10,8 @@ package excelize import ( - "bytes" "encoding/xml" "fmt" - "io" "math" "strconv" ) @@ -30,95 +28,35 @@ import ( // } // func (f *File) GetRows(sheet string) ([][]string, error) { - name, ok := f.sheetMap[trimSheetName(sheet)] - if !ok { - return nil, nil - } - - xlsx, err := f.workSheetReader(sheet) + rows, err := f.Rows(sheet) if err != nil { return nil, err } - if xlsx != nil { - output, _ := xml.Marshal(f.Sheet[name]) - f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output)) - } - - xml.NewDecoder(bytes.NewReader(f.readXML(name))) - d := f.sharedStringsReader() - var ( - inElement string - rowData xlsxRow - ) - - rowCount, colCount, err := f.getTotalRowsCols(name) - if err != nil { - return nil, nil - } - rows := make([][]string, rowCount) - for i := range rows { - rows[i] = make([]string, colCount) - } - - var row int - decoder := xml.NewDecoder(bytes.NewReader(f.readXML(name))) - for { - token, _ := decoder.Token() - if token == nil { + results := make([][]string, 0, 64) + for rows.Next() { + if rows.Error() != nil { break } - switch startElement := token.(type) { - case xml.StartElement: - inElement = startElement.Name.Local - if inElement == "row" { - rowData = xlsxRow{} - _ = decoder.DecodeElement(&rowData, &startElement) - cr := rowData.R - 1 - for _, colCell := range rowData.C { - col, _, err := CellNameToCoordinates(colCell.R) - if err != nil { - return nil, err - } - val, _ := colCell.getValueFrom(f, d) - rows[cr][col-1] = val - if val != "" { - row = rowData.R - } - } - } - default: + row, err := rows.Columns() + if err != nil { + break } + results = append(results, row) } - return rows[:row], nil + return results, nil } // Rows defines an iterator to a sheet type Rows struct { - decoder *xml.Decoder - token xml.Token - err error - f *File + err error + f *File + rows []xlsxRow + curRow int } // 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 - } - } - } + return rows.curRow < len(rows.rows) } // Error will return the error when the find next row element @@ -128,15 +66,12 @@ func (rows *Rows) Error() error { // Columns return the current row's column values func (rows *Rows) Columns() ([]string, error) { - if rows.token == nil { - return []string{}, nil - } - startElement := rows.token.(xml.StartElement) - r := xlsxRow{} - _ = rows.decoder.DecodeElement(&r, &startElement) + curRow := rows.rows[rows.curRow] + rows.curRow++ + + columns := make([]string, len(curRow.C)) d := rows.f.sharedStringsReader() - columns := make([]string, len(r.C)) - for _, colCell := range r.C { + for _, colCell := range curRow.C { col, _, err := CellNameToCoordinates(colCell.R) if err != nil { return columns, err @@ -181,46 +116,11 @@ func (f *File) Rows(sheet string) (*Rows, error) { f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output)) } return &Rows{ - f: f, - decoder: xml.NewDecoder(bytes.NewReader(f.readXML(name))), + f: f, + rows: xlsx.SheetData.Row, }, nil } -// getTotalRowsCols provides a function to get total columns and rows in a -// worksheet. -func (f *File) getTotalRowsCols(name string) (int, int, error) { - decoder := xml.NewDecoder(bytes.NewReader(f.readXML(name))) - var inElement string - var r xlsxRow - var tr, tc int - for { - token, _ := decoder.Token() - if token == nil { - break - } - switch startElement := token.(type) { - case xml.StartElement: - inElement = startElement.Name.Local - if inElement == "row" { - r = xlsxRow{} - _ = decoder.DecodeElement(&r, &startElement) - tr = r.R - for _, colCell := range r.C { - col, _, err := CellNameToCoordinates(colCell.R) - if err != nil { - return tr, tc, err - } - if col > tc { - tc = col - } - } - } - default: - } - } - return tr, tc, nil -} - // SetRowHeight provides a function to set the height of a single row. For // example, set the height of the first row in Sheet1: // diff --git a/rows_test.go b/rows_test.go index f7d49b4695116fc7deee792ae2ec808370032315..d52c635590038df73b1b14b064b35993e8b6b5cd 100644 --- a/rows_test.go +++ b/rows_test.go @@ -39,9 +39,6 @@ func TestRows(t *testing.T) { if !assert.Equal(t, collectedRows, returnedRows) { t.FailNow() } - - r := Rows{} - r.Columns() } func TestRowsError(t *testing.T) { @@ -672,6 +669,21 @@ func TestDuplicateRowInvalidRownum(t *testing.T) { } } +func BenchmarkRows(b *testing.B) { + for i := 0; i < b.N; i++ { + f, _ := OpenFile(filepath.Join("test", "Book1.xlsx")) + rows, _ := f.Rows("Sheet2") + for rows.Next() { + row, _ := rows.Columns() + for i := range row { + if i >= 0 { + continue + } + } + } + } +} + func trimSliceSpace(s []string) []string { for { if len(s) > 0 && s[len(s)-1] == "" {