Merge pull request #456 from mlh758/fix-439

Fix #439
This commit is contained in:
xuri 2019-08-05 22:18:31 +08:00 committed by GitHub
commit 451e6e384a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 36 additions and 124 deletions

142
rows.go
View File

@ -10,10 +10,8 @@
package excelize package excelize
import ( import (
"bytes"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"io"
"math" "math"
"strconv" "strconv"
) )
@ -30,95 +28,35 @@ import (
// } // }
// //
func (f *File) GetRows(sheet string) ([][]string, error) { func (f *File) GetRows(sheet string) ([][]string, error) {
name, ok := f.sheetMap[trimSheetName(sheet)] rows, err := f.Rows(sheet)
if !ok {
return nil, nil
}
xlsx, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if xlsx != nil { results := make([][]string, 0, 64)
output, _ := xml.Marshal(f.Sheet[name]) for rows.Next() {
f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output)) if rows.Error() != nil {
}
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 {
break break
} }
switch startElement := token.(type) { row, err := rows.Columns()
case xml.StartElement: if err != nil {
inElement = startElement.Name.Local break
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:
} }
results = append(results, row)
} }
return rows[:row], nil return results, nil
} }
// Rows defines an iterator to a sheet // Rows defines an iterator to a sheet
type Rows struct { type Rows struct {
decoder *xml.Decoder err error
token xml.Token f *File
err error rows []xlsxRow
f *File curRow int
} }
// Next will return true if find the next row element. // Next will return true if find the next row element.
func (rows *Rows) Next() bool { func (rows *Rows) Next() bool {
for { return rows.curRow < len(rows.rows)
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 // 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 // Columns return the current row's column values
func (rows *Rows) Columns() ([]string, error) { func (rows *Rows) Columns() ([]string, error) {
if rows.token == nil { curRow := rows.rows[rows.curRow]
return []string{}, nil rows.curRow++
}
startElement := rows.token.(xml.StartElement) columns := make([]string, len(curRow.C))
r := xlsxRow{}
_ = rows.decoder.DecodeElement(&r, &startElement)
d := rows.f.sharedStringsReader() d := rows.f.sharedStringsReader()
columns := make([]string, len(r.C)) for _, colCell := range curRow.C {
for _, colCell := range r.C {
col, _, err := CellNameToCoordinates(colCell.R) col, _, err := CellNameToCoordinates(colCell.R)
if err != nil { if err != nil {
return columns, err return columns, err
@ -181,46 +116,11 @@ func (f *File) Rows(sheet string) (*Rows, error) {
f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output)) f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output))
} }
return &Rows{ return &Rows{
f: f, f: f,
decoder: xml.NewDecoder(bytes.NewReader(f.readXML(name))), rows: xlsx.SheetData.Row,
}, nil }, 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 // SetRowHeight provides a function to set the height of a single row. For
// example, set the height of the first row in Sheet1: // example, set the height of the first row in Sheet1:
// //

View File

@ -39,9 +39,6 @@ func TestRows(t *testing.T) {
if !assert.Equal(t, collectedRows, returnedRows) { if !assert.Equal(t, collectedRows, returnedRows) {
t.FailNow() t.FailNow()
} }
r := Rows{}
r.Columns()
} }
func TestRowsError(t *testing.T) { 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 { func trimSliceSpace(s []string) []string {
for { for {
if len(s) > 0 && s[len(s)-1] == "" { if len(s) > 0 && s[len(s)-1] == "" {