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 |