|
@@ -13,8 +13,8 @@ import (
|
|
|
"bytes"
|
|
"bytes"
|
|
|
"encoding/xml"
|
|
"encoding/xml"
|
|
|
"errors"
|
|
"errors"
|
|
|
- "fmt"
|
|
|
|
|
"math"
|
|
"math"
|
|
|
|
|
+ "strconv"
|
|
|
"strings"
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/mohae/deepcopy"
|
|
"github.com/mohae/deepcopy"
|
|
@@ -34,10 +34,11 @@ type Cols struct {
|
|
|
sheet string
|
|
sheet string
|
|
|
cols []xlsxCols
|
|
cols []xlsxCols
|
|
|
f *File
|
|
f *File
|
|
|
- decoder *xml.Decoder
|
|
|
|
|
|
|
+ sheetXML []byte
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// GetCols return all the columns in a sheet by given worksheet name (case sensitive). For example:
|
|
|
|
|
|
|
+// GetCols return all the columns in a sheet by given worksheet name (case
|
|
|
|
|
+// sensitive). For example:
|
|
|
//
|
|
//
|
|
|
// cols, err := f.Cols("Sheet1")
|
|
// cols, err := f.Cols("Sheet1")
|
|
|
// if err != nil {
|
|
// if err != nil {
|
|
@@ -60,29 +61,17 @@ func (f *File) GetCols(sheet string) ([][]string, error) {
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
results := make([][]string, 0, 64)
|
|
results := make([][]string, 0, 64)
|
|
|
-
|
|
|
|
|
for cols.Next() {
|
|
for cols.Next() {
|
|
|
- if cols.Error() != nil {
|
|
|
|
|
- break
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- col, err := cols.Rows()
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- break
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ col, _ := cols.Rows()
|
|
|
results = append(results, col)
|
|
results = append(results, col)
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
return results, nil
|
|
return results, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Next will return true if the next col element is found.
|
|
// Next will return true if the next col element is found.
|
|
|
func (cols *Cols) Next() bool {
|
|
func (cols *Cols) Next() bool {
|
|
|
cols.curCol++
|
|
cols.curCol++
|
|
|
-
|
|
|
|
|
return cols.curCol <= cols.totalCol
|
|
return cols.curCol <= cols.totalCol
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -91,27 +80,53 @@ func (cols *Cols) Error() error {
|
|
|
return cols.err
|
|
return cols.err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Rows return the current column's row values
|
|
|
|
|
|
|
+// Rows return the current column's row values.
|
|
|
func (cols *Cols) Rows() ([]string, error) {
|
|
func (cols *Cols) Rows() ([]string, error) {
|
|
|
var (
|
|
var (
|
|
|
- err error
|
|
|
|
|
- rows []string
|
|
|
|
|
|
|
+ err error
|
|
|
|
|
+ inElement string
|
|
|
|
|
+ cellCol, cellRow int
|
|
|
|
|
+ rows []string
|
|
|
)
|
|
)
|
|
|
-
|
|
|
|
|
if cols.stashCol >= cols.curCol {
|
|
if cols.stashCol >= cols.curCol {
|
|
|
return rows, err
|
|
return rows, err
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- for i := 1; i <= cols.totalRow; i++ {
|
|
|
|
|
- colName, _ := ColumnNumberToName(cols.curCol)
|
|
|
|
|
- val, _ := cols.f.GetCellValue(cols.sheet, fmt.Sprintf("%s%d", colName, i))
|
|
|
|
|
- rows = append(rows, val)
|
|
|
|
|
|
|
+ d := cols.f.sharedStringsReader()
|
|
|
|
|
+ decoder := cols.f.xmlNewDecoder(bytes.NewReader(cols.sheetXML))
|
|
|
|
|
+ for {
|
|
|
|
|
+ token, _ := decoder.Token()
|
|
|
|
|
+ if token == nil {
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ switch startElement := token.(type) {
|
|
|
|
|
+ case xml.StartElement:
|
|
|
|
|
+ inElement = startElement.Name.Local
|
|
|
|
|
+ if inElement == "c" {
|
|
|
|
|
+ for _, attr := range startElement.Attr {
|
|
|
|
|
+ if attr.Name.Local == "r" {
|
|
|
|
|
+ if cellCol, cellRow, err = CellNameToCoordinates(attr.Value); err != nil {
|
|
|
|
|
+ return rows, err
|
|
|
|
|
+ }
|
|
|
|
|
+ blank := cellRow - len(rows)
|
|
|
|
|
+ for i := 1; i < blank; i++ {
|
|
|
|
|
+ rows = append(rows, "")
|
|
|
|
|
+ }
|
|
|
|
|
+ if cellCol == cols.curCol {
|
|
|
|
|
+ colCell := xlsxC{}
|
|
|
|
|
+ _ = decoder.DecodeElement(&colCell, &startElement)
|
|
|
|
|
+ val, _ := colCell.getValueFrom(cols.f, d)
|
|
|
|
|
+ rows = append(rows, val)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
return rows, nil
|
|
return rows, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Cols returns a columns iterator, used for streaming/reading data for a worksheet with a large data. For example:
|
|
|
|
|
|
|
+// Cols returns a columns iterator, used for streaming/reading data for a
|
|
|
|
|
+// worksheet with a large data. For example:
|
|
|
//
|
|
//
|
|
|
// cols, err := f.Cols("Sheet1")
|
|
// cols, err := f.Cols("Sheet1")
|
|
|
// if err != nil {
|
|
// if err != nil {
|
|
@@ -134,60 +149,51 @@ func (f *File) Cols(sheet string) (*Cols, error) {
|
|
|
if !ok {
|
|
if !ok {
|
|
|
return nil, ErrSheetNotExist{sheet}
|
|
return nil, ErrSheetNotExist{sheet}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
if f.Sheet[name] != nil {
|
|
if f.Sheet[name] != nil {
|
|
|
output, _ := xml.Marshal(f.Sheet[name])
|
|
output, _ := xml.Marshal(f.Sheet[name])
|
|
|
f.saveFileList(name, replaceRelationshipsNameSpaceBytes(output))
|
|
f.saveFileList(name, replaceRelationshipsNameSpaceBytes(output))
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
var (
|
|
var (
|
|
|
- inElement string
|
|
|
|
|
- cols Cols
|
|
|
|
|
- colsNum, rowsNum []int
|
|
|
|
|
|
|
+ inElement string
|
|
|
|
|
+ cols Cols
|
|
|
|
|
+ cellCol int
|
|
|
|
|
+ err error
|
|
|
)
|
|
)
|
|
|
- decoder := f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
|
|
|
|
|
-
|
|
|
|
|
|
|
+ cols.sheetXML = f.readXML(name)
|
|
|
|
|
+ decoder := f.xmlNewDecoder(bytes.NewReader(cols.sheetXML))
|
|
|
for {
|
|
for {
|
|
|
token, _ := decoder.Token()
|
|
token, _ := decoder.Token()
|
|
|
if token == nil {
|
|
if token == nil {
|
|
|
break
|
|
break
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
switch startElement := token.(type) {
|
|
switch startElement := token.(type) {
|
|
|
case xml.StartElement:
|
|
case xml.StartElement:
|
|
|
inElement = startElement.Name.Local
|
|
inElement = startElement.Name.Local
|
|
|
- if inElement == "dimension" {
|
|
|
|
|
- colsNum = make([]int, 0)
|
|
|
|
|
- rowsNum = make([]int, 0)
|
|
|
|
|
-
|
|
|
|
|
|
|
+ if inElement == "row" {
|
|
|
for _, attr := range startElement.Attr {
|
|
for _, attr := range startElement.Attr {
|
|
|
- if attr.Name.Local == "ref" {
|
|
|
|
|
- sheetCoordinates := attr.Value
|
|
|
|
|
- if i := strings.Index(sheetCoordinates, ":"); i <= -1 {
|
|
|
|
|
- return &cols, errors.New("Sheet coordinates are wrong")
|
|
|
|
|
|
|
+ if attr.Name.Local == "r" {
|
|
|
|
|
+ if cols.totalRow, err = strconv.Atoi(attr.Value); err != nil {
|
|
|
|
|
+ return &cols, err
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- coordinates := strings.Split(sheetCoordinates, ":")
|
|
|
|
|
- for _, coordinate := range coordinates {
|
|
|
|
|
- c, r, _ := SplitCellName(coordinate)
|
|
|
|
|
- columnNum, _ := ColumnNameToNumber(c)
|
|
|
|
|
- colsNum = append(colsNum, columnNum)
|
|
|
|
|
- rowsNum = append(rowsNum, r)
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if inElement == "c" {
|
|
|
|
|
+ for _, attr := range startElement.Attr {
|
|
|
|
|
+ if attr.Name.Local == "r" {
|
|
|
|
|
+ if cellCol, _, err = CellNameToCoordinates(attr.Value); err != nil {
|
|
|
|
|
+ return &cols, err
|
|
|
|
|
+ }
|
|
|
|
|
+ if cellCol > cols.totalCol {
|
|
|
|
|
+ cols.totalCol = cellCol
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- cols.totalCol = colsNum[1] - (colsNum[0] - 1)
|
|
|
|
|
- cols.totalRow = rowsNum[1] - (rowsNum[0] - 1)
|
|
|
|
|
}
|
|
}
|
|
|
- default:
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
cols.f = f
|
|
cols.f = f
|
|
|
cols.sheet = trimSheetName(sheet)
|
|
cols.sheet = trimSheetName(sheet)
|
|
|
- cols.decoder = f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
|
|
|
|
|
-
|
|
|
|
|
return &cols, nil
|
|
return &cols, nil
|
|
|
}
|
|
}
|
|
|
|
|
|