forked from p30928647/excelize
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
This commit is contained in:
parent
cbe919fdf6
commit
ac91ca0ded
142
rows.go
142
rows.go
|
@ -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:
|
||||||
//
|
//
|
||||||
|
|
18
rows_test.go
18
rows_test.go
|
@ -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] == "" {
|
||||||
|
|
Loading…
Reference in New Issue