diff --git a/README.md b/README.md index 89d2d001..6ab549ed 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ func main() { fmt.Println(err) } }() - // Get value from cell by given worksheet name and axis. + // Get value from cell by given worksheet name and cell reference. cell, err := f.GetCellValue("Sheet1", "B2") if err != nil { fmt.Println(err) diff --git a/adjust.go b/adjust.go index 3a0271da..92efcd0a 100644 --- a/adjust.go +++ b/adjust.go @@ -185,7 +185,7 @@ func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection, Decode(&t); err != nil && err != io.EOF { return } - coordinates, err := areaRefToCoordinates(t.Ref) + coordinates, err := rangeRefToCoordinates(t.Ref) if err != nil { return } @@ -204,7 +204,7 @@ func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection, idx-- continue } - t.Ref, _ = f.coordinatesToAreaRef([]int{x1, y1, x2, y2}) + t.Ref, _ = f.coordinatesToRangeRef([]int{x1, y1, x2, y2}) if t.AutoFilter != nil { t.AutoFilter.Ref = t.Ref } @@ -221,7 +221,7 @@ func (f *File) adjustAutoFilter(ws *xlsxWorksheet, dir adjustDirection, num, off return nil } - coordinates, err := areaRefToCoordinates(ws.AutoFilter.Ref) + coordinates, err := rangeRefToCoordinates(ws.AutoFilter.Ref) if err != nil { return err } @@ -241,7 +241,7 @@ func (f *File) adjustAutoFilter(ws *xlsxWorksheet, dir adjustDirection, num, off coordinates = f.adjustAutoFilterHelper(dir, coordinates, num, offset) x1, y1, x2, y2 = coordinates[0], coordinates[1], coordinates[2], coordinates[3] - if ws.AutoFilter.Ref, err = f.coordinatesToAreaRef([]int{x1, y1, x2, y2}); err != nil { + if ws.AutoFilter.Ref, err = f.coordinatesToRangeRef([]int{x1, y1, x2, y2}); err != nil { return err } return nil @@ -277,8 +277,8 @@ func (f *File) adjustMergeCells(ws *xlsxWorksheet, dir adjustDirection, num, off } for i := 0; i < len(ws.MergeCells.Cells); i++ { - areaData := ws.MergeCells.Cells[i] - coordinates, err := areaRefToCoordinates(areaData.Ref) + mergedCells := ws.MergeCells.Cells[i] + coordinates, err := rangeRefToCoordinates(mergedCells.Ref) if err != nil { return err } @@ -305,8 +305,8 @@ func (f *File) adjustMergeCells(ws *xlsxWorksheet, dir adjustDirection, num, off i-- continue } - areaData.rect = []int{x1, y1, x2, y2} - if areaData.Ref, err = f.coordinatesToAreaRef([]int{x1, y1, x2, y2}); err != nil { + mergedCells.rect = []int{x1, y1, x2, y2} + if mergedCells.Ref, err = f.coordinatesToRangeRef([]int{x1, y1, x2, y2}); err != nil { return err } } diff --git a/calc.go b/calc.go index f7b3a630..b19dba74 100644 --- a/calc.go +++ b/calc.go @@ -789,10 +789,14 @@ func (f *File) calcCellValue(ctx *calcContext, sheet, cell string) (result strin return } result = token.Value() - isNum, precision := isNumeric(result) - if isNum && (precision > 15 || precision == 0) { - num := roundPrecision(result, -1) - result = strings.ToUpper(num) + if isNum, precision, decimal := isNumeric(result); isNum { + if precision > 15 { + result = strings.ToUpper(strconv.FormatFloat(decimal, 'G', 15, 64)) + return + } + if !strings.HasPrefix(result, "0") { + result = strings.ToUpper(strconv.FormatFloat(decimal, 'f', -1, 64)) + } } return } @@ -2089,13 +2093,13 @@ func (fn *formulaFuncs) COMPLEX(argsList *list.List) formulaArg { // cmplx2str replace complex number string characters. func cmplx2str(num complex128, suffix string) string { realPart, imagPart := fmt.Sprint(real(num)), fmt.Sprint(imag(num)) - isNum, i := isNumeric(realPart) + isNum, i, decimal := isNumeric(realPart) if isNum && i > 15 { - realPart = roundPrecision(realPart, -1) + realPart = strconv.FormatFloat(decimal, 'G', 15, 64) } - isNum, i = isNumeric(imagPart) + isNum, i, decimal = isNumeric(imagPart) if isNum && i > 15 { - imagPart = roundPrecision(imagPart, -1) + imagPart = strconv.FormatFloat(decimal, 'G', 15, 64) } c := realPart if imag(num) > 0 { diff --git a/calc_test.go b/calc_test.go index ef196dca..df86f907 100644 --- a/calc_test.go +++ b/calc_test.go @@ -1736,7 +1736,7 @@ func TestCalcCellValue(t *testing.T) { "=UPPER(\"TEST 123\")": "TEST 123", // VALUE "=VALUE(\"50\")": "50", - "=VALUE(\"1.0E-07\")": "1E-07", + "=VALUE(\"1.0E-07\")": "0.0000001", "=VALUE(\"5,000\")": "5000", "=VALUE(\"20%\")": "0.2", "=VALUE(\"12:00:00\")": "0.5", diff --git a/cell.go b/cell.go index b97c4109..6beb3b27 100644 --- a/cell.go +++ b/cell.go @@ -485,7 +485,7 @@ func (f *File) SetCellDefault(sheet, cell, value string) error { // setCellDefault prepares cell type and string type cell value by a given // string. func setCellDefault(value string) (t string, v string) { - if ok, _ := isNumeric(value); !ok { + if ok, _, _ := isNumeric(value); !ok { t = "str" } v = value @@ -631,7 +631,7 @@ func (f *File) SetCellFormula(sheet, cell, formula string, opts ...FormulaOpts) // setSharedFormula set shared formula for the cells. func (ws *xlsxWorksheet) setSharedFormula(ref string) error { - coordinates, err := areaRefToCoordinates(ref) + coordinates, err := rangeRefToCoordinates(ref) if err != nil { return err } @@ -1098,7 +1098,7 @@ func (f *File) setSheetCells(sheet, cell string, slice interface{}, dir adjustDi return err } -// getCellInfo does common preparation for all SetCell* methods. +// getCellInfo does common preparation for all set cell value functions. func (f *File) prepareCell(ws *xlsxWorksheet, cell string) (*xlsxC, int, int, error) { var err error cell, err = f.mergeCellsParser(ws, cell) @@ -1116,8 +1116,9 @@ func (f *File) prepareCell(ws *xlsxWorksheet, cell string) (*xlsxC, int, int, er return &ws.SheetData.Row[row-1].C[col-1], col, row, err } -// getCellStringFunc does common value extraction workflow for all GetCell* -// methods. Passed function implements specific part of required logic. +// getCellStringFunc does common value extraction workflow for all get cell +// value function. Passed function implements specific part of required +// logic. func (f *File) getCellStringFunc(sheet, cell string, fn func(x *xlsxWorksheet, c *xlsxC) (string, bool, error)) (string, error) { ws, err := f.workSheetReader(sheet) if err != nil { @@ -1235,7 +1236,7 @@ func (f *File) mergeCellsParser(ws *xlsxWorksheet, cell string) (string, error) i-- continue } - ok, err := f.checkCellInArea(cell, ws.MergeCells.Cells[i].Ref) + ok, err := f.checkCellInRangeRef(cell, ws.MergeCells.Cells[i].Ref) if err != nil { return cell, err } @@ -1247,18 +1248,18 @@ func (f *File) mergeCellsParser(ws *xlsxWorksheet, cell string) (string, error) return cell, nil } -// checkCellInArea provides a function to determine if a given cell reference +// checkCellInRangeRef provides a function to determine if a given cell reference // in a range. -func (f *File) checkCellInArea(cell, area string) (bool, error) { +func (f *File) checkCellInRangeRef(cell, reference string) (bool, error) { col, row, err := CellNameToCoordinates(cell) if err != nil { return false, err } - if rng := strings.Split(area, ":"); len(rng) != 2 { + if rng := strings.Split(reference, ":"); len(rng) != 2 { return false, err } - coordinates, err := areaRefToCoordinates(area) + coordinates, err := rangeRefToCoordinates(reference) if err != nil { return false, err } @@ -1333,7 +1334,7 @@ func parseSharedFormula(dCol, dRow int, orig []byte) (res string, start int) { // R1C1-reference notation, are the same. // // Note that this function not validate ref tag to check the cell whether in -// allow area, and always return origin shared formula. +// allow range reference, and always return origin shared formula. func getSharedFormula(ws *xlsxWorksheet, si int, cell string) string { for _, r := range ws.SheetData.Row { for _, c := range r.C { diff --git a/cell_test.go b/cell_test.go index 9c8b511d..02057051 100644 --- a/cell_test.go +++ b/cell_test.go @@ -95,43 +95,43 @@ func TestConcurrency(t *testing.T) { assert.NoError(t, f.Close()) } -func TestCheckCellInArea(t *testing.T) { +func TestCheckCellInRangeRef(t *testing.T) { f := NewFile() - expectedTrueCellInAreaList := [][2]string{ + expectedTrueCellInRangeRefList := [][2]string{ {"c2", "A1:AAZ32"}, {"B9", "A1:B9"}, {"C2", "C2:C2"}, } - for _, expectedTrueCellInArea := range expectedTrueCellInAreaList { - cell := expectedTrueCellInArea[0] - area := expectedTrueCellInArea[1] - ok, err := f.checkCellInArea(cell, area) + for _, expectedTrueCellInRangeRef := range expectedTrueCellInRangeRefList { + cell := expectedTrueCellInRangeRef[0] + reference := expectedTrueCellInRangeRef[1] + ok, err := f.checkCellInRangeRef(cell, reference) assert.NoError(t, err) assert.Truef(t, ok, - "Expected cell %v to be in area %v, got false\n", cell, area) + "Expected cell %v to be in range reference %v, got false\n", cell, reference) } - expectedFalseCellInAreaList := [][2]string{ + expectedFalseCellInRangeRefList := [][2]string{ {"c2", "A4:AAZ32"}, {"C4", "D6:A1"}, // weird case, but you never know {"AEF42", "BZ40:AEF41"}, } - for _, expectedFalseCellInArea := range expectedFalseCellInAreaList { - cell := expectedFalseCellInArea[0] - area := expectedFalseCellInArea[1] - ok, err := f.checkCellInArea(cell, area) + for _, expectedFalseCellInRangeRef := range expectedFalseCellInRangeRefList { + cell := expectedFalseCellInRangeRef[0] + reference := expectedFalseCellInRangeRef[1] + ok, err := f.checkCellInRangeRef(cell, reference) assert.NoError(t, err) assert.Falsef(t, ok, - "Expected cell %v not to be inside of area %v, but got true\n", cell, area) + "Expected cell %v not to be inside of range reference %v, but got true\n", cell, reference) } - ok, err := f.checkCellInArea("A1", "A:B") + ok, err := f.checkCellInRangeRef("A1", "A:B") assert.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) assert.False(t, ok) - ok, err = f.checkCellInArea("AA0", "Z0:AB1") + ok, err = f.checkCellInRangeRef("AA0", "Z0:AB1") assert.EqualError(t, err, newCellNameToCoordinatesError("AA0", newInvalidCellNameError("AA0")).Error()) assert.False(t, ok) } @@ -326,8 +326,10 @@ func TestGetCellValue(t *testing.T) { 275.39999999999998 68.900000000000006 1.1000000000000001 - 1234567890123_4 - 123456789_0123_4 + 1234567890123_4 + 123456789_0123_4 + +0.0000000000000000002399999999999992E-4 + 7.2399999999999992E-2 `))) f.checked = nil rows, err = f.GetRows("Sheet1") @@ -361,6 +363,8 @@ func TestGetCellValue(t *testing.T) { "1.1", "1234567890123_4", "123456789_0123_4", + "2.39999999999999E-23", + "0.0724", }}, rows) assert.NoError(t, err) } diff --git a/col.go b/col.go index adc7f85e..964cb975 100644 --- a/col.go +++ b/col.go @@ -560,7 +560,7 @@ func flatCols(col xlsxCol, cols []xlsxCol, replacer func(fc, c xlsxCol) xlsxCol) // | | | (x2,y2)| // +-----+------------+------------+ // -// Example of an object that covers some area from cell A1 to B2. +// Example of an object that covers some range reference from cell A1 to B2. // // Based on the width and height of the object we need to calculate 8 vars: // diff --git a/datavalidation.go b/datavalidation.go index 3d82f7c5..5ae5f659 100644 --- a/datavalidation.go +++ b/datavalidation.go @@ -333,7 +333,7 @@ func (f *File) squashSqref(cells [][]int) []string { l, r := 0, 0 for i := 1; i < len(cells); i++ { if cells[i][0] == cells[r][0] && cells[i][1]-cells[r][1] > 1 { - curr, _ := f.coordinatesToAreaRef(append(cells[l], cells[r]...)) + curr, _ := f.coordinatesToRangeRef(append(cells[l], cells[r]...)) if l == r { curr, _ = CoordinatesToCellName(cells[l][0], cells[l][1]) } @@ -343,7 +343,7 @@ func (f *File) squashSqref(cells [][]int) []string { r++ } } - curr, _ := f.coordinatesToAreaRef(append(cells[l], cells[r]...)) + curr, _ := f.coordinatesToRangeRef(append(cells[l], cells[r]...)) if l == r { curr, _ = CoordinatesToCellName(cells[l][0], cells[l][1]) } diff --git a/excelize_test.go b/excelize_test.go index 5756e6ea..9bb6fa83 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -615,7 +615,7 @@ func TestSetCellStyleBorder(t *testing.T) { var style int - // Test set border on overlapping area with vertical variants shading styles gradient fill. + // Test set border on overlapping range with vertical variants shading styles gradient fill. style, err = f.NewStyle(&Style{ Border: []Border{ {Type: "left", Color: "0000FF", Style: 3}, diff --git a/lib.go b/lib.go index 5feb7d59..21ce7d2c 100644 --- a/lib.go +++ b/lib.go @@ -19,6 +19,7 @@ import ( "fmt" "io" "io/ioutil" + "math/big" "os" "regexp" "strconv" @@ -273,19 +274,19 @@ func CoordinatesToCellName(col, row int, abs ...bool) (string, error) { return sign + colName + sign + strconv.Itoa(row), err } -// areaRefToCoordinates provides a function to convert range reference to a +// rangeRefToCoordinates provides a function to convert range reference to a // pair of coordinates. -func areaRefToCoordinates(ref string) ([]int, error) { +func rangeRefToCoordinates(ref string) ([]int, error) { rng := strings.Split(strings.ReplaceAll(ref, "$", ""), ":") if len(rng) < 2 { return nil, ErrParameterInvalid } - return areaRangeToCoordinates(rng[0], rng[1]) + return cellRefsToCoordinates(rng[0], rng[1]) } -// areaRangeToCoordinates provides a function to convert cell range to a +// cellRefsToCoordinates provides a function to convert cell range to a // pair of coordinates. -func areaRangeToCoordinates(firstCell, lastCell string) ([]int, error) { +func cellRefsToCoordinates(firstCell, lastCell string) ([]int, error) { coordinates := make([]int, 4) var err error coordinates[0], coordinates[1], err = CellNameToCoordinates(firstCell) @@ -311,9 +312,9 @@ func sortCoordinates(coordinates []int) error { return nil } -// coordinatesToAreaRef provides a function to convert a pair of coordinates -// to area reference. -func (f *File) coordinatesToAreaRef(coordinates []int) (string, error) { +// coordinatesToRangeRef provides a function to convert a pair of coordinates +// to range reference. +func (f *File) coordinatesToRangeRef(coordinates []int) (string, error) { if len(coordinates) != 4 { return "", ErrCoordinates } @@ -364,7 +365,7 @@ func (f *File) flatSqref(sqref string) (cells map[int][][]int, err error) { } cells[col] = append(cells[col], []int{col, row}) case 2: - if coordinates, err = areaRefToCoordinates(ref); err != nil { + if coordinates, err = rangeRefToCoordinates(ref); err != nil { return } _ = sortCoordinates(coordinates) @@ -691,34 +692,16 @@ func (f *File) addSheetNameSpace(sheet string, ns xml.Attr) { // isNumeric determines whether an expression is a valid numeric type and get // the precision for the numeric. -func isNumeric(s string) (bool, int) { - dot, e, n, p := false, false, false, 0 - for i, v := range s { - if v == '.' { - if dot { - return false, 0 - } - dot = true - } else if v == 'E' || v == 'e' { - e = true - } else if v < '0' || v > '9' { - if i == 0 && v == '-' { - continue - } - if e && v == '-' { - return true, 0 - } - if e && v == '+' { - p = 15 - continue - } - return false, 0 - } else { - p++ - } - n = true +func isNumeric(s string) (bool, int, float64) { + var decimal big.Float + _, ok := decimal.SetString(s) + if !ok { + return false, 0, 0 } - return n, p + var noScientificNotation string + flt, _ := decimal.Float64() + noScientificNotation = strconv.FormatFloat(flt, 'f', -1, 64) + return true, len(strings.ReplaceAll(noScientificNotation, ".", "")), flt } var ( diff --git a/lib_test.go b/lib_test.go index c42914d8..e96704f6 100644 --- a/lib_test.go +++ b/lib_test.go @@ -217,15 +217,15 @@ func TestCoordinatesToCellName_Error(t *testing.T) { } } -func TestCoordinatesToAreaRef(t *testing.T) { +func TestCoordinatesToRangeRef(t *testing.T) { f := NewFile() - _, err := f.coordinatesToAreaRef([]int{}) + _, err := f.coordinatesToRangeRef([]int{}) assert.EqualError(t, err, ErrCoordinates.Error()) - _, err = f.coordinatesToAreaRef([]int{1, -1, 1, 1}) + _, err = f.coordinatesToRangeRef([]int{1, -1, 1, 1}) assert.EqualError(t, err, "invalid cell reference [1, -1]") - _, err = f.coordinatesToAreaRef([]int{1, 1, 1, -1}) + _, err = f.coordinatesToRangeRef([]int{1, 1, 1, -1}) assert.EqualError(t, err, "invalid cell reference [1, -1]") - ref, err := f.coordinatesToAreaRef([]int{1, 1, 1, 1}) + ref, err := f.coordinatesToRangeRef([]int{1, 1, 1, 1}) assert.NoError(t, err) assert.EqualValues(t, ref, "A1:A1") } diff --git a/merge.go b/merge.go index ac7fb047..04dc493d 100644 --- a/merge.go +++ b/merge.go @@ -17,7 +17,7 @@ import "strings" func (mc *xlsxMergeCell) Rect() ([]int, error) { var err error if mc.rect == nil { - mc.rect, err = areaRefToCoordinates(mc.Ref) + mc.rect, err = rangeRefToCoordinates(mc.Ref) } return mc.rect, err } @@ -46,7 +46,7 @@ func (mc *xlsxMergeCell) Rect() ([]int, error) { // |A8(x3,y4) C8(x4,y4)| // +------------------------+ func (f *File) MergeCell(sheet, hCell, vCell string) error { - rect, err := areaRefToCoordinates(hCell + ":" + vCell) + rect, err := rangeRefToCoordinates(hCell + ":" + vCell) if err != nil { return err } @@ -73,11 +73,11 @@ func (f *File) MergeCell(sheet, hCell, vCell string) error { } // UnmergeCell provides a function to unmerge a given range reference. -// For example unmerge area D3:E9 on Sheet1: +// For example unmerge range reference D3:E9 on Sheet1: // // err := f.UnmergeCell("Sheet1", "D3", "E9") // -// Attention: overlapped areas will also be unmerged. +// Attention: overlapped range will also be unmerged. func (f *File) UnmergeCell(sheet, hCell, vCell string) error { ws, err := f.workSheetReader(sheet) if err != nil { @@ -85,7 +85,7 @@ func (f *File) UnmergeCell(sheet, hCell, vCell string) error { } ws.Lock() defer ws.Unlock() - rect1, err := areaRefToCoordinates(hCell + ":" + vCell) + rect1, err := rangeRefToCoordinates(hCell + ":" + vCell) if err != nil { return err } @@ -105,7 +105,7 @@ func (f *File) UnmergeCell(sheet, hCell, vCell string) error { if mergeCell == nil { continue } - rect2, _ := areaRefToCoordinates(mergeCell.Ref) + rect2, _ := rangeRefToCoordinates(mergeCell.Ref) if isOverlap(rect1, rect2) { continue } diff --git a/merge_test.go b/merge_test.go index f4d5f7ed..6977c5ac 100644 --- a/merge_test.go +++ b/merge_test.go @@ -169,7 +169,7 @@ func TestUnmergeCell(t *testing.T) { f = NewFile() assert.NoError(t, f.MergeCell("Sheet1", "A2", "B3")) - // Test unmerged area on not exists worksheet. + // Test unmerged range reference on not exists worksheet. assert.EqualError(t, f.UnmergeCell("SheetN", "A1", "A1"), "sheet SheetN does not exist") ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml") diff --git a/numfmt.go b/numfmt.go index fd99240b..09e64c96 100644 --- a/numfmt.go +++ b/numfmt.go @@ -279,7 +279,7 @@ var ( // prepareNumberic split the number into two before and after parts by a // decimal point. func (nf *numberFormat) prepareNumberic(value string) { - if nf.isNumeric, _ = isNumeric(value); !nf.isNumeric { + if nf.isNumeric, _, _ = isNumeric(value); !nf.isNumeric { return } } @@ -338,13 +338,13 @@ func (nf *numberFormat) positiveHandler() (result string) { continue } if token.TType == nfp.TokenTypeZeroPlaceHolder && token.TValue == strings.Repeat("0", len(token.TValue)) { - if isNum, precision := isNumeric(nf.value); isNum { + if isNum, precision, decimal := isNumeric(nf.value); isNum { if nf.number < 1 { nf.result += "0" continue } if precision > 15 { - nf.result += roundPrecision(nf.value, 15) + nf.result += strconv.FormatFloat(decimal, 'f', -1, 64) } else { nf.result += fmt.Sprintf("%.f", nf.number) } @@ -902,13 +902,13 @@ func (nf *numberFormat) negativeHandler() (result string) { continue } if token.TType == nfp.TokenTypeZeroPlaceHolder && token.TValue == strings.Repeat("0", len(token.TValue)) { - if isNum, precision := isNumeric(nf.value); isNum { + if isNum, precision, decimal := isNumeric(nf.value); isNum { if math.Abs(nf.number) < 1 { nf.result += "0" continue } if precision > 15 { - nf.result += strings.TrimLeft(roundPrecision(nf.value, 15), "-") + nf.result += strings.TrimLeft(strconv.FormatFloat(decimal, 'f', -1, 64), "-") } else { nf.result += fmt.Sprintf("%.f", math.Abs(nf.number)) } @@ -941,7 +941,7 @@ func (nf *numberFormat) textHandler() (result string) { // getValueSectionType returns its applicable number format expression section // based on the given value. func (nf *numberFormat) getValueSectionType(value string) (float64, string) { - isNum, _ := isNumeric(value) + isNum, _, _ := isNumeric(value) if !isNum { return 0, nfp.TokenSectionText } diff --git a/picture.go b/picture.go index 3b6d8213..e1c62f2e 100644 --- a/picture.go +++ b/picture.go @@ -652,11 +652,11 @@ func (f *File) drawingResize(sheet, cell string, width, height float64, formatSe if inMergeCell { continue } - if inMergeCell, err = f.checkCellInArea(cell, mergeCell[0]); err != nil { + if inMergeCell, err = f.checkCellInRangeRef(cell, mergeCell[0]); err != nil { return } if inMergeCell { - rng, _ = areaRangeToCoordinates(mergeCell.GetStartAxis(), mergeCell.GetEndAxis()) + rng, _ = cellRefsToCoordinates(mergeCell.GetStartAxis(), mergeCell.GetEndAxis()) _ = sortCoordinates(rng) } } diff --git a/pivotTable.go b/pivotTable.go index af30a0b7..8e16e068 100644 --- a/pivotTable.go +++ b/pivotTable.go @@ -81,8 +81,9 @@ type PivotTableField struct { // options. Note that the same fields can not in Columns, Rows and Filter // fields at the same time. // -// For example, create a pivot table on the Sheet1!$G$2:$M$34 area with the -// region Sheet1!$A$1:$E$31 as the data source, summarize by sum for sales: +// For example, create a pivot table on the Sheet1!$G$2:$M$34 range reference +// with the region Sheet1!$A$1:$E$31 as the data source, summarize by sum for +// sales: // // package main // @@ -205,7 +206,7 @@ func (f *File) adjustRange(rangeStr string) (string, []int, error) { return "", []int{}, ErrParameterInvalid } trimRng := strings.ReplaceAll(rng[1], "$", "") - coordinates, err := areaRefToCoordinates(trimRng) + coordinates, err := rangeRefToCoordinates(trimRng) if err != nil { return rng[0], []int{}, err } diff --git a/rows.go b/rows.go index 561f64b2..2960aa46 100644 --- a/rows.go +++ b/rows.go @@ -19,7 +19,6 @@ import ( "io/ioutil" "log" "math" - "math/big" "os" "strconv" @@ -486,32 +485,17 @@ func (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (string, error) { } return f.formattedValue(c.S, c.V, raw), nil default: - if isNum, precision := isNumeric(c.V); isNum && !raw { - if precision == 0 { - c.V = roundPrecision(c.V, 15) + if isNum, precision, decimal := isNumeric(c.V); isNum && !raw { + if precision > 15 { + c.V = strconv.FormatFloat(decimal, 'G', 15, 64) } else { - c.V = roundPrecision(c.V, -1) + c.V = strconv.FormatFloat(decimal, 'f', -1, 64) } } return f.formattedValue(c.S, c.V, raw), nil } } -// roundPrecision provides a function to format floating-point number text -// with precision, if the given text couldn't be parsed to float, this will -// return the original string. -func roundPrecision(text string, prec int) string { - decimal := big.Float{} - if _, ok := decimal.SetString(text); ok { - flt, _ := decimal.Float64() - if prec == -1 { - return strconv.FormatFloat(flt, 'G', 15, 64) - } - return strconv.FormatFloat(flt, 'f', -1, 64) - } - return text -} - // SetRowVisible provides a function to set visible of a single row by given // worksheet name and Excel row number. For example, hide row 2 in Sheet1: // @@ -732,7 +716,7 @@ func (f *File) duplicateMergeCells(sheet string, ws *xlsxWorksheet, row, row2 in row++ } for _, rng := range ws.MergeCells.Cells { - coordinates, err := areaRefToCoordinates(rng.Ref) + coordinates, err := rangeRefToCoordinates(rng.Ref) if err != nil { return err } @@ -741,8 +725,8 @@ func (f *File) duplicateMergeCells(sheet string, ws *xlsxWorksheet, row, row2 in } } for i := 0; i < len(ws.MergeCells.Cells); i++ { - areaData := ws.MergeCells.Cells[i] - coordinates, _ := areaRefToCoordinates(areaData.Ref) + mergedCells := ws.MergeCells.Cells[i] + coordinates, _ := rangeRefToCoordinates(mergedCells.Ref) x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3] if y1 == y2 && y1 == row { from, _ := CoordinatesToCellName(x1, row2) diff --git a/rows_test.go b/rows_test.go index d51e2568..8ce007ff 100644 --- a/rows_test.go +++ b/rows_test.go @@ -995,10 +995,6 @@ func TestNumberFormats(t *testing.T) { assert.NoError(t, f.Close()) } -func TestRoundPrecision(t *testing.T) { - assert.Equal(t, "text", roundPrecision("text", 0)) -} - func BenchmarkRows(b *testing.B) { f, _ := OpenFile(filepath.Join("test", "Book1.xlsx")) for i := 0; i < b.N; i++ { diff --git a/sheet.go b/sheet.go index ee4f6675..fe24b18c 100644 --- a/sheet.go +++ b/sheet.go @@ -39,6 +39,9 @@ import ( // `Sheet1` will be created. func (f *File) NewSheet(sheet string) int { // Check if the worksheet already exists + if trimSheetName(sheet) == "" { + return -1 + } index := f.GetSheetIndex(sheet) if index != -1 { return index diff --git a/sheet_test.go b/sheet_test.go index 324d626b..87c36d46 100644 --- a/sheet_test.go +++ b/sheet_test.go @@ -90,6 +90,8 @@ func TestNewSheet(t *testing.T) { assert.NoError(t, f.SaveAs(filepath.Join("test", "TestNewSheet.xlsx"))) // create new worksheet with already exists name assert.Equal(t, f.GetSheetIndex("Sheet2"), f.NewSheet("Sheet2")) + // create new worksheet with empty sheet name + assert.Equal(t, -1, f.NewSheet(":\\/?*[]")) } func TestSetPane(t *testing.T) { diff --git a/stream.go b/stream.go index 84299cbf..019731f9 100644 --- a/stream.go +++ b/stream.go @@ -145,7 +145,7 @@ func (sw *StreamWriter) AddTable(hCell, vCell, format string) error { return err } - coordinates, err := areaRangeToCoordinates(hCell, vCell) + coordinates, err := cellRefsToCoordinates(hCell, vCell) if err != nil { return err } @@ -157,7 +157,7 @@ func (sw *StreamWriter) AddTable(hCell, vCell, format string) error { } // Correct table reference range, such correct C1:B3 to B1:C3. - ref, err := sw.File.coordinatesToAreaRef(coordinates) + ref, err := sw.File.coordinatesToRangeRef(coordinates) if err != nil { return err } @@ -412,7 +412,7 @@ func (sw *StreamWriter) SetColWidth(min, max int, width float64) error { // the StreamWriter. Don't create a merged cell that overlaps with another // existing merged cell. func (sw *StreamWriter) MergeCell(hCell, vCell string) error { - _, err := areaRangeToCoordinates(hCell, vCell) + _, err := cellRefsToCoordinates(hCell, vCell) if err != nil { return err } diff --git a/styles.go b/styles.go index 587fd749..a4f5dc48 100644 --- a/styles.go +++ b/styles.go @@ -2486,11 +2486,14 @@ func (f *File) GetCellStyle(sheet, cell string) (int, error) { if err != nil { return 0, err } - c, col, row, err := f.prepareCell(ws, cell) + col, row, err := CellNameToCoordinates(cell) if err != nil { return 0, err } - return f.prepareCellStyle(ws, col, row, c.S), err + prepareSheetXML(ws, col, row) + ws.Lock() + defer ws.Unlock() + return f.prepareCellStyle(ws, col, row, ws.SheetData.Row[row-1].C[col-1].S), err } // SetCellStyle provides a function to add style attribute for cells by given @@ -2856,7 +2859,7 @@ func (f *File) SetCellStyle(sheet, hCell, vCell string, styleID int) error { // max_color - Same as min_color, see above. // // bar_color - Used for data_bar. Same as min_color, see above. -func (f *File) SetConditionalFormat(sheet, area, formatSet string) error { +func (f *File) SetConditionalFormat(sheet, reference, formatSet string) error { var format []*formatConditional err := json.Unmarshal([]byte(formatSet), &format) if err != nil { @@ -2897,7 +2900,7 @@ func (f *File) SetConditionalFormat(sheet, area, formatSet string) error { } ws.ConditionalFormatting = append(ws.ConditionalFormatting, &xlsxConditionalFormatting{ - SQRef: area, + SQRef: reference, CfRule: cfRule, }) return err diff --git a/table.go b/table.go index 66ba7297..7ef75625 100644 --- a/table.go +++ b/table.go @@ -48,7 +48,7 @@ func parseFormatTableSet(formatSet string) (*formatTable, error) { // Note that the table must be at least two lines including the header. The // header cells must contain strings and must be unique, and must set the // header row data of the table before calling the AddTable function. Multiple -// tables coordinate areas that can't have an intersection. +// tables range reference that can't have an intersection. // // table_name: The name of the table, in the same worksheet name of the table should be unique // @@ -167,7 +167,7 @@ func (f *File) addTable(sheet, tableXML string, x1, y1, x2, y2, i int, formatSet } // Correct table range reference, such correct C1:B3 to B1:C3. - ref, err := f.coordinatesToAreaRef([]int{x1, y1, x2, y2}) + ref, err := f.coordinatesToRangeRef([]int{x1, y1, x2, y2}) if err != nil { return err }