未验证 提交 ac91ca0d 编写于 作者: H Harris

Only parse xml once when reading

We were parsing the whole sheet twice since the
sheet reader already reads in all the rows.

getTotalRowsCols function is unused after these changes
so it has been deleted as well.

Closes #439
上级 cbe919fd
...@@ -10,10 +10,8 @@ ...@@ -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 ( ...@@ -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 { ...@@ -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) { ...@@ -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:
// //
......
...@@ -39,9 +39,6 @@ func TestRows(t *testing.T) { ...@@ -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) { ...@@ -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] == "" {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册