diff options
| -rw-r--r-- | cell.go | 3 | ||||
| -rw-r--r-- | col.go | 120 | ||||
| -rw-r--r-- | col_test.go | 53 | ||||
| -rw-r--r-- | lib.go | 3 | ||||
| -rw-r--r-- | rows.go | 3 | 
5 files changed, 106 insertions, 76 deletions
| @@ -41,9 +41,6 @@ var rwMutex sync.RWMutex  func (f *File) GetCellValue(sheet, axis string) (string, error) {  	return f.getCellStringFunc(sheet, axis, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {  		val, err := c.getValueFrom(f, f.sharedStringsReader()) -		if err != nil { -			return val, false, err -		}  		return val, true, err  	})  } @@ -13,8 +13,8 @@ import (  	"bytes"  	"encoding/xml"  	"errors" -	"fmt"  	"math" +	"strconv"  	"strings"  	"github.com/mohae/deepcopy" @@ -34,10 +34,11 @@ type Cols struct {  	sheet                                string  	cols                                 []xlsxCols  	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")  //    if err != nil { @@ -60,29 +61,17 @@ func (f *File) GetCols(sheet string) ([][]string, error) {  	if err != nil {  		return nil, err  	} -  	results := make([][]string, 0, 64) -  	for cols.Next() { -		if cols.Error() != nil { -			break -		} - -		col, err := cols.Rows() -		if err != nil { -			break -		} - +		col, _ := cols.Rows()  		results = append(results, col)  	} -  	return results, nil  }  // Next will return true if the next col element is found.  func (cols *Cols) Next() bool {  	cols.curCol++ -  	return cols.curCol <= cols.totalCol  } @@ -91,27 +80,53 @@ func (cols *Cols) Error() error {  	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) {  	var ( -		err  error -		rows []string +		err              error +		inElement        string +		cellCol, cellRow int +		rows             []string  	) -  	if cols.stashCol >= cols.curCol {  		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  } -// 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")  //    if err != nil { @@ -134,60 +149,51 @@ func (f *File) Cols(sheet string) (*Cols, error) {  	if !ok {  		return nil, ErrSheetNotExist{sheet}  	} -  	if f.Sheet[name] != nil {  		output, _ := xml.Marshal(f.Sheet[name])  		f.saveFileList(name, replaceRelationshipsNameSpaceBytes(output))  	} -  	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 {  		token, _ := decoder.Token()  		if token == nil {  			break  		} -  		switch startElement := token.(type) {  		case xml.StartElement:  			inElement = startElement.Name.Local -			if inElement == "dimension" { -				colsNum = make([]int, 0) -				rowsNum = make([]int, 0) - +			if inElement == "row" {  				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.sheet = trimSheetName(sheet) -	cols.decoder = f.xmlNewDecoder(bytes.NewReader(f.readXML(name))) -  	return &cols, nil  } diff --git a/col_test.go b/col_test.go index a130f2b..fac78eb 100644 --- a/col_test.go +++ b/col_test.go @@ -1,7 +1,6 @@  package excelize  import ( -	"bytes"  	"path/filepath"  	"testing" @@ -61,7 +60,7 @@ func TestCols(t *testing.T) {  func TestColumnsIterator(t *testing.T) {  	const (  		sheet2         = "Sheet2" -		expectedNumCol = 4 +		expectedNumCol = 9  	)  	f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) @@ -82,29 +81,57 @@ func TestColumnsIterator(t *testing.T) {  	for _, cell := range cells {  		assert.NoError(t, f.SetCellValue("Sheet1", cell, 1))  	} -	f.Sheet["xl/worksheets/sheet1.xml"] = &xlsxWorksheet{ -		Dimension: &xlsxDimension{ -			Ref: "C2:D4", -		}, -	}  	cols, err = f.Cols("Sheet1")  	require.NoError(t, err)  	colCount = 0  	for cols.Next() {  		colCount++ -		require.True(t, colCount <= 2, "colCount is greater than expected") +		require.True(t, colCount <= 4, "colCount is greater than expected")  	} -	assert.Equal(t, 2, colCount) +	assert.Equal(t, 4, colCount)  }  func TestColsError(t *testing.T) { -	xlsx, err := OpenFile(filepath.Join("test", "Book1.xlsx")) +	f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) +	if !assert.NoError(t, err) { +		t.FailNow() +	} +	_, err = f.Cols("SheetN") +	assert.EqualError(t, err, "sheet SheetN is not exist") +} + +func TestGetColsError(t *testing.T) { +	f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))  	if !assert.NoError(t, err) {  		t.FailNow()  	} -	_, err = xlsx.Cols("SheetN") +	_, err = f.GetCols("SheetN")  	assert.EqualError(t, err, "sheet SheetN is not exist") + +	f = NewFile() +	delete(f.Sheet, "xl/worksheets/sheet1.xml") +	f.XLSX["xl/worksheets/sheet1.xml"] = []byte(`<worksheet><sheetData><row r="A"><c r="2" t="str"><v>B</v></c></row></sheetData></worksheet>`) +	f.checked = nil +	_, err = f.GetCols("Sheet1") +	assert.EqualError(t, err, `strconv.Atoi: parsing "A": invalid syntax`) + +	f = NewFile() +	delete(f.Sheet, "xl/worksheets/sheet1.xml") +	f.XLSX["xl/worksheets/sheet1.xml"] = []byte(`<worksheet><sheetData><row r="2"><c r="A" t="str"><v>B</v></c></row></sheetData></worksheet>`) +	f.checked = nil +	_, err = f.GetCols("Sheet1") +	assert.EqualError(t, err, `cannot convert cell "A" to coordinates: invalid cell name "A"`) + +	f = NewFile() +	cols, err := f.Cols("Sheet1") +	assert.NoError(t, err) +	cols.totalRow = 2 +	cols.totalCol = 2 +	cols.curCol = 1 +	cols.decoder = []byte(`<worksheet><sheetData><row r="1"><c r="A" t="str"><v>A</v></c></row></sheetData></worksheet>`) +	_, err = cols.Rows() +	assert.EqualError(t, err, `cannot convert cell "A" to coordinates: invalid cell name "A"`)  }  func TestColsRows(t *testing.T) { @@ -112,7 +139,7 @@ func TestColsRows(t *testing.T) {  	f.NewSheet("Sheet1")  	cols, err := f.Cols("Sheet1") -	assert.EqualError(t, err, `Sheet coordinates are wrong`) +	assert.NoError(t, err)  	assert.NoError(t, f.SetCellValue("Sheet1", "A1", 1))  	f.Sheet["xl/worksheets/sheet1.xml"] = &xlsxWorksheet{ @@ -126,7 +153,7 @@ func TestColsRows(t *testing.T) {  	assert.NoError(t, err)  	// Test if token is nil -	cols.decoder = f.xmlNewDecoder(bytes.NewReader(nil)) +	cols.decoder = nil  	_, err = cols.Rows()  	assert.NoError(t, err)  } @@ -152,6 +152,9 @@ func ColumnNumberToName(num int) (string, error) {  	if num < 1 {  		return "", fmt.Errorf("incorrect column number %d", num)  	} +	if num > TotalColumns { +		return "", fmt.Errorf("column number exceeds maximum limit") +	}  	var col string  	for num > 0 {  		col = string((num-1)%26+65) + col @@ -46,9 +46,6 @@ func (f *File) GetRows(sheet string) ([][]string, error) {  	}  	results := make([][]string, 0, 64)  	for rows.Next() { -		if rows.Error() != nil { -			break -		}  		row, err := rows.Columns()  		if err != nil {  			break | 
