diff --git a/excelize.go b/excelize.go index 4d46b94..ba6445f 100644 --- a/excelize.go +++ b/excelize.go @@ -155,20 +155,12 @@ func checkSheet(xlsx *xlsxWorksheet) { row = lastRow } } - sheetData := xlsxSheetData{} - existsRows := map[int]int{} - for k := range xlsx.SheetData.Row { - existsRows[xlsx.SheetData.Row[k].R] = k + sheetData := xlsxSheetData{Row: make([]xlsxRow, row)} + for _, r := range xlsx.SheetData.Row { + sheetData.Row[r.R-1] = r } - for i := 0; i < row; i++ { - _, ok := existsRows[i+1] - if ok { - sheetData.Row = append(sheetData.Row, xlsx.SheetData.Row[existsRows[i+1]]) - } else { - sheetData.Row = append(sheetData.Row, xlsxRow{ - R: i + 1, - }) - } + for i := 1; i <= row; i++ { + sheetData.Row[i-1].R = i } xlsx.SheetData = sheetData } diff --git a/file_test.go b/file_test.go new file mode 100644 index 0000000..6c30f4a --- /dev/null +++ b/file_test.go @@ -0,0 +1,27 @@ +package excelize + +import ( + "testing" +) + +func BenchmarkWrite(b *testing.B) { + const s = "This is test data" + for i := 0; i < b.N; i++ { + f := NewFile() + for row := 1; row <= 10000; row++ { + for col := 1; col <= 20; col++ { + val, err := CoordinatesToCellName(col, row) + if err != nil { + panic(err) + } + f.SetCellDefault("Sheet1", val, s) + } + } + // Save xlsx file by the given path. + err := f.SaveAs("./test.xlsx") + if err != nil { + panic(err) + } + } + +} diff --git a/sheet.go b/sheet.go index 335c4fc..43c7cc0 100644 --- a/sheet.go +++ b/sheet.go @@ -117,12 +117,19 @@ func (f *File) workSheetWriter() { } } -// trimCell provides a function to trim blank cells which created by completeCol. +// trimCell provides a function to trim blank cells which created by fillColumns. func trimCell(column []xlsxC) []xlsxC { + rowFull := true + for i := range column { + rowFull = column[i].hasValue() && rowFull + } + if rowFull { + return column + } col := make([]xlsxC, len(column)) i := 0 for _, c := range column { - if c.S != 0 || c.V != "" || c.F != nil || c.T != "" { + if c.hasValue() { col[i] = c i++ } @@ -1404,12 +1411,17 @@ func (f *File) relsReader(path string) *xlsxRelationships { // fillSheetData ensures there are enough rows, and columns in the chosen // row to accept data. Missing rows are backfilled and given their row number +// Uses the last populated row as a hint for the size of the next row to add func prepareSheetXML(xlsx *xlsxWorksheet, col int, row int) { rowCount := len(xlsx.SheetData.Row) + sizeHint := 0 + if rowCount > 0 { + sizeHint = len(xlsx.SheetData.Row[rowCount-1].C) + } if rowCount < row { // append missing rows for rowIdx := rowCount; rowIdx < row; rowIdx++ { - xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{R: rowIdx + 1}) + xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{R: rowIdx + 1, C: make([]xlsxC, 0, sizeHint)}) } } rowData := &xlsx.SheetData.Row[row-1] diff --git a/xmlWorksheet.go b/xmlWorksheet.go index 96ca235..8408cfa 100644 --- a/xmlWorksheet.go +++ b/xmlWorksheet.go @@ -430,6 +430,10 @@ type xlsxC struct { XMLSpace xml.Attr `xml:"space,attr,omitempty"` } +func (c *xlsxC) hasValue() bool { + return c.S != 0 || c.V != "" || c.F != nil || c.T != "" +} + // xlsxF directly maps the f element in the namespace // http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have // not checked it for completeness - it does as much as I need.