This closes #1360, closes #1361

- Fix default number format parse issue with a long string of digits
- Fix creating a sheet with an empty name cause a corrupted file
- The `GetCellStyle` function no longer return master cell style of the merge cell range
- Using the specialized name in variables and functions
This commit is contained in:
xuri 2022-09-28 00:04:17 +08:00
parent addcc1a0b2
commit efcf599dfe
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
23 changed files with 126 additions and 145 deletions

View File

@ -83,7 +83,7 @@ func main() {
fmt.Println(err) 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") cell, err := f.GetCellValue("Sheet1", "B2")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)

View File

@ -185,7 +185,7 @@ func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection,
Decode(&t); err != nil && err != io.EOF { Decode(&t); err != nil && err != io.EOF {
return return
} }
coordinates, err := areaRefToCoordinates(t.Ref) coordinates, err := rangeRefToCoordinates(t.Ref)
if err != nil { if err != nil {
return return
} }
@ -204,7 +204,7 @@ func (f *File) adjustTable(ws *xlsxWorksheet, sheet string, dir adjustDirection,
idx-- idx--
continue continue
} }
t.Ref, _ = f.coordinatesToAreaRef([]int{x1, y1, x2, y2}) t.Ref, _ = f.coordinatesToRangeRef([]int{x1, y1, x2, y2})
if t.AutoFilter != nil { if t.AutoFilter != nil {
t.AutoFilter.Ref = t.Ref t.AutoFilter.Ref = t.Ref
} }
@ -221,7 +221,7 @@ func (f *File) adjustAutoFilter(ws *xlsxWorksheet, dir adjustDirection, num, off
return nil return nil
} }
coordinates, err := areaRefToCoordinates(ws.AutoFilter.Ref) coordinates, err := rangeRefToCoordinates(ws.AutoFilter.Ref)
if err != nil { if err != nil {
return err return err
} }
@ -241,7 +241,7 @@ func (f *File) adjustAutoFilter(ws *xlsxWorksheet, dir adjustDirection, num, off
coordinates = f.adjustAutoFilterHelper(dir, coordinates, num, offset) coordinates = f.adjustAutoFilterHelper(dir, coordinates, num, offset)
x1, y1, x2, y2 = coordinates[0], coordinates[1], coordinates[2], coordinates[3] 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 err
} }
return nil 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++ { for i := 0; i < len(ws.MergeCells.Cells); i++ {
areaData := ws.MergeCells.Cells[i] mergedCells := ws.MergeCells.Cells[i]
coordinates, err := areaRefToCoordinates(areaData.Ref) coordinates, err := rangeRefToCoordinates(mergedCells.Ref)
if err != nil { if err != nil {
return err return err
} }
@ -305,8 +305,8 @@ func (f *File) adjustMergeCells(ws *xlsxWorksheet, dir adjustDirection, num, off
i-- i--
continue continue
} }
areaData.rect = []int{x1, y1, x2, y2} mergedCells.rect = []int{x1, y1, x2, y2}
if areaData.Ref, err = f.coordinatesToAreaRef([]int{x1, y1, x2, y2}); err != nil { if mergedCells.Ref, err = f.coordinatesToRangeRef([]int{x1, y1, x2, y2}); err != nil {
return err return err
} }
} }

20
calc.go
View File

@ -789,10 +789,14 @@ func (f *File) calcCellValue(ctx *calcContext, sheet, cell string) (result strin
return return
} }
result = token.Value() result = token.Value()
isNum, precision := isNumeric(result) if isNum, precision, decimal := isNumeric(result); isNum {
if isNum && (precision > 15 || precision == 0) { if precision > 15 {
num := roundPrecision(result, -1) result = strings.ToUpper(strconv.FormatFloat(decimal, 'G', 15, 64))
result = strings.ToUpper(num) return
}
if !strings.HasPrefix(result, "0") {
result = strings.ToUpper(strconv.FormatFloat(decimal, 'f', -1, 64))
}
} }
return return
} }
@ -2089,13 +2093,13 @@ func (fn *formulaFuncs) COMPLEX(argsList *list.List) formulaArg {
// cmplx2str replace complex number string characters. // cmplx2str replace complex number string characters.
func cmplx2str(num complex128, suffix string) string { func cmplx2str(num complex128, suffix string) string {
realPart, imagPart := fmt.Sprint(real(num)), fmt.Sprint(imag(num)) realPart, imagPart := fmt.Sprint(real(num)), fmt.Sprint(imag(num))
isNum, i := isNumeric(realPart) isNum, i, decimal := isNumeric(realPart)
if isNum && i > 15 { 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 { if isNum && i > 15 {
imagPart = roundPrecision(imagPart, -1) imagPart = strconv.FormatFloat(decimal, 'G', 15, 64)
} }
c := realPart c := realPart
if imag(num) > 0 { if imag(num) > 0 {

View File

@ -1736,7 +1736,7 @@ func TestCalcCellValue(t *testing.T) {
"=UPPER(\"TEST 123\")": "TEST 123", "=UPPER(\"TEST 123\")": "TEST 123",
// VALUE // VALUE
"=VALUE(\"50\")": "50", "=VALUE(\"50\")": "50",
"=VALUE(\"1.0E-07\")": "1E-07", "=VALUE(\"1.0E-07\")": "0.0000001",
"=VALUE(\"5,000\")": "5000", "=VALUE(\"5,000\")": "5000",
"=VALUE(\"20%\")": "0.2", "=VALUE(\"20%\")": "0.2",
"=VALUE(\"12:00:00\")": "0.5", "=VALUE(\"12:00:00\")": "0.5",

23
cell.go
View File

@ -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 // setCellDefault prepares cell type and string type cell value by a given
// string. // string.
func setCellDefault(value string) (t string, v string) { func setCellDefault(value string) (t string, v string) {
if ok, _ := isNumeric(value); !ok { if ok, _, _ := isNumeric(value); !ok {
t = "str" t = "str"
} }
v = value v = value
@ -631,7 +631,7 @@ func (f *File) SetCellFormula(sheet, cell, formula string, opts ...FormulaOpts)
// setSharedFormula set shared formula for the cells. // setSharedFormula set shared formula for the cells.
func (ws *xlsxWorksheet) setSharedFormula(ref string) error { func (ws *xlsxWorksheet) setSharedFormula(ref string) error {
coordinates, err := areaRefToCoordinates(ref) coordinates, err := rangeRefToCoordinates(ref)
if err != nil { if err != nil {
return err return err
} }
@ -1098,7 +1098,7 @@ func (f *File) setSheetCells(sheet, cell string, slice interface{}, dir adjustDi
return err 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) { func (f *File) prepareCell(ws *xlsxWorksheet, cell string) (*xlsxC, int, int, error) {
var err error var err error
cell, err = f.mergeCellsParser(ws, cell) 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 return &ws.SheetData.Row[row-1].C[col-1], col, row, err
} }
// getCellStringFunc does common value extraction workflow for all GetCell* // getCellStringFunc does common value extraction workflow for all get cell
// methods. Passed function implements specific part of required logic. // 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) { func (f *File) getCellStringFunc(sheet, cell string, fn func(x *xlsxWorksheet, c *xlsxC) (string, bool, error)) (string, error) {
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
@ -1235,7 +1236,7 @@ func (f *File) mergeCellsParser(ws *xlsxWorksheet, cell string) (string, error)
i-- i--
continue continue
} }
ok, err := f.checkCellInArea(cell, ws.MergeCells.Cells[i].Ref) ok, err := f.checkCellInRangeRef(cell, ws.MergeCells.Cells[i].Ref)
if err != nil { if err != nil {
return cell, err return cell, err
} }
@ -1247,18 +1248,18 @@ func (f *File) mergeCellsParser(ws *xlsxWorksheet, cell string) (string, error)
return cell, nil 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. // 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) col, row, err := CellNameToCoordinates(cell)
if err != nil { if err != nil {
return false, err return false, err
} }
if rng := strings.Split(area, ":"); len(rng) != 2 { if rng := strings.Split(reference, ":"); len(rng) != 2 {
return false, err return false, err
} }
coordinates, err := areaRefToCoordinates(area) coordinates, err := rangeRefToCoordinates(reference)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -1333,7 +1334,7 @@ func parseSharedFormula(dCol, dRow int, orig []byte) (res string, start int) {
// R1C1-reference notation, are the same. // R1C1-reference notation, are the same.
// //
// Note that this function not validate ref tag to check the cell whether in // 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 { func getSharedFormula(ws *xlsxWorksheet, si int, cell string) string {
for _, r := range ws.SheetData.Row { for _, r := range ws.SheetData.Row {
for _, c := range r.C { for _, c := range r.C {

View File

@ -95,43 +95,43 @@ func TestConcurrency(t *testing.T) {
assert.NoError(t, f.Close()) assert.NoError(t, f.Close())
} }
func TestCheckCellInArea(t *testing.T) { func TestCheckCellInRangeRef(t *testing.T) {
f := NewFile() f := NewFile()
expectedTrueCellInAreaList := [][2]string{ expectedTrueCellInRangeRefList := [][2]string{
{"c2", "A1:AAZ32"}, {"c2", "A1:AAZ32"},
{"B9", "A1:B9"}, {"B9", "A1:B9"},
{"C2", "C2:C2"}, {"C2", "C2:C2"},
} }
for _, expectedTrueCellInArea := range expectedTrueCellInAreaList { for _, expectedTrueCellInRangeRef := range expectedTrueCellInRangeRefList {
cell := expectedTrueCellInArea[0] cell := expectedTrueCellInRangeRef[0]
area := expectedTrueCellInArea[1] reference := expectedTrueCellInRangeRef[1]
ok, err := f.checkCellInArea(cell, area) ok, err := f.checkCellInRangeRef(cell, reference)
assert.NoError(t, err) assert.NoError(t, err)
assert.Truef(t, ok, 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"}, {"c2", "A4:AAZ32"},
{"C4", "D6:A1"}, // weird case, but you never know {"C4", "D6:A1"}, // weird case, but you never know
{"AEF42", "BZ40:AEF41"}, {"AEF42", "BZ40:AEF41"},
} }
for _, expectedFalseCellInArea := range expectedFalseCellInAreaList { for _, expectedFalseCellInRangeRef := range expectedFalseCellInRangeRefList {
cell := expectedFalseCellInArea[0] cell := expectedFalseCellInRangeRef[0]
area := expectedFalseCellInArea[1] reference := expectedFalseCellInRangeRef[1]
ok, err := f.checkCellInArea(cell, area) ok, err := f.checkCellInRangeRef(cell, reference)
assert.NoError(t, err) assert.NoError(t, err)
assert.Falsef(t, ok, 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.EqualError(t, err, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
assert.False(t, ok) 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.EqualError(t, err, newCellNameToCoordinatesError("AA0", newInvalidCellNameError("AA0")).Error())
assert.False(t, ok) assert.False(t, ok)
} }
@ -326,8 +326,10 @@ func TestGetCellValue(t *testing.T) {
<c r="Y1"><v>275.39999999999998</v></c> <c r="Y1"><v>275.39999999999998</v></c>
<c r="Z1"><v>68.900000000000006</v></c> <c r="Z1"><v>68.900000000000006</v></c>
<c r="AA1"><v>1.1000000000000001</v></c> <c r="AA1"><v>1.1000000000000001</v></c>
<c r="AA2"><v>1234567890123_4</v></c> <c r="AB1" t="str"><v>1234567890123_4</v></c>
<c r="AA3"><v>123456789_0123_4</v></c> <c r="AC1" t="str"><v>123456789_0123_4</v></c>
<c r="AD1"><v>+0.0000000000000000002399999999999992E-4</v></c>
<c r="AE1"><v>7.2399999999999992E-2</v></c>
</row>`))) </row>`)))
f.checked = nil f.checked = nil
rows, err = f.GetRows("Sheet1") rows, err = f.GetRows("Sheet1")
@ -361,6 +363,8 @@ func TestGetCellValue(t *testing.T) {
"1.1", "1.1",
"1234567890123_4", "1234567890123_4",
"123456789_0123_4", "123456789_0123_4",
"2.39999999999999E-23",
"0.0724",
}}, rows) }}, rows)
assert.NoError(t, err) assert.NoError(t, err)
} }

2
col.go
View File

@ -560,7 +560,7 @@ func flatCols(col xlsxCol, cols []xlsxCol, replacer func(fc, c xlsxCol) xlsxCol)
// | | | (x2,y2)| // | | | (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: // Based on the width and height of the object we need to calculate 8 vars:
// //

View File

@ -333,7 +333,7 @@ func (f *File) squashSqref(cells [][]int) []string {
l, r := 0, 0 l, r := 0, 0
for i := 1; i < len(cells); i++ { for i := 1; i < len(cells); i++ {
if cells[i][0] == cells[r][0] && cells[i][1]-cells[r][1] > 1 { 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 { if l == r {
curr, _ = CoordinatesToCellName(cells[l][0], cells[l][1]) curr, _ = CoordinatesToCellName(cells[l][0], cells[l][1])
} }
@ -343,7 +343,7 @@ func (f *File) squashSqref(cells [][]int) []string {
r++ r++
} }
} }
curr, _ := f.coordinatesToAreaRef(append(cells[l], cells[r]...)) curr, _ := f.coordinatesToRangeRef(append(cells[l], cells[r]...))
if l == r { if l == r {
curr, _ = CoordinatesToCellName(cells[l][0], cells[l][1]) curr, _ = CoordinatesToCellName(cells[l][0], cells[l][1])
} }

View File

@ -615,7 +615,7 @@ func TestSetCellStyleBorder(t *testing.T) {
var style int 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{ style, err = f.NewStyle(&Style{
Border: []Border{ Border: []Border{
{Type: "left", Color: "0000FF", Style: 3}, {Type: "left", Color: "0000FF", Style: 3},

55
lib.go
View File

@ -19,6 +19,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"math/big"
"os" "os"
"regexp" "regexp"
"strconv" "strconv"
@ -273,19 +274,19 @@ func CoordinatesToCellName(col, row int, abs ...bool) (string, error) {
return sign + colName + sign + strconv.Itoa(row), err 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. // pair of coordinates.
func areaRefToCoordinates(ref string) ([]int, error) { func rangeRefToCoordinates(ref string) ([]int, error) {
rng := strings.Split(strings.ReplaceAll(ref, "$", ""), ":") rng := strings.Split(strings.ReplaceAll(ref, "$", ""), ":")
if len(rng) < 2 { if len(rng) < 2 {
return nil, ErrParameterInvalid 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. // pair of coordinates.
func areaRangeToCoordinates(firstCell, lastCell string) ([]int, error) { func cellRefsToCoordinates(firstCell, lastCell string) ([]int, error) {
coordinates := make([]int, 4) coordinates := make([]int, 4)
var err error var err error
coordinates[0], coordinates[1], err = CellNameToCoordinates(firstCell) coordinates[0], coordinates[1], err = CellNameToCoordinates(firstCell)
@ -311,9 +312,9 @@ func sortCoordinates(coordinates []int) error {
return nil return nil
} }
// coordinatesToAreaRef provides a function to convert a pair of coordinates // coordinatesToRangeRef provides a function to convert a pair of coordinates
// to area reference. // to range reference.
func (f *File) coordinatesToAreaRef(coordinates []int) (string, error) { func (f *File) coordinatesToRangeRef(coordinates []int) (string, error) {
if len(coordinates) != 4 { if len(coordinates) != 4 {
return "", ErrCoordinates 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}) cells[col] = append(cells[col], []int{col, row})
case 2: case 2:
if coordinates, err = areaRefToCoordinates(ref); err != nil { if coordinates, err = rangeRefToCoordinates(ref); err != nil {
return return
} }
_ = sortCoordinates(coordinates) _ = 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 // isNumeric determines whether an expression is a valid numeric type and get
// the precision for the numeric. // the precision for the numeric.
func isNumeric(s string) (bool, int) { func isNumeric(s string) (bool, int, float64) {
dot, e, n, p := false, false, false, 0 var decimal big.Float
for i, v := range s { _, ok := decimal.SetString(s)
if v == '.' { if !ok {
if dot { return false, 0, 0
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
} }
return n, p var noScientificNotation string
flt, _ := decimal.Float64()
noScientificNotation = strconv.FormatFloat(flt, 'f', -1, 64)
return true, len(strings.ReplaceAll(noScientificNotation, ".", "")), flt
} }
var ( var (

View File

@ -217,15 +217,15 @@ func TestCoordinatesToCellName_Error(t *testing.T) {
} }
} }
func TestCoordinatesToAreaRef(t *testing.T) { func TestCoordinatesToRangeRef(t *testing.T) {
f := NewFile() f := NewFile()
_, err := f.coordinatesToAreaRef([]int{}) _, err := f.coordinatesToRangeRef([]int{})
assert.EqualError(t, err, ErrCoordinates.Error()) 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]") 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]") 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.NoError(t, err)
assert.EqualValues(t, ref, "A1:A1") assert.EqualValues(t, ref, "A1:A1")
} }

View File

@ -17,7 +17,7 @@ import "strings"
func (mc *xlsxMergeCell) Rect() ([]int, error) { func (mc *xlsxMergeCell) Rect() ([]int, error) {
var err error var err error
if mc.rect == nil { if mc.rect == nil {
mc.rect, err = areaRefToCoordinates(mc.Ref) mc.rect, err = rangeRefToCoordinates(mc.Ref)
} }
return mc.rect, err return mc.rect, err
} }
@ -46,7 +46,7 @@ func (mc *xlsxMergeCell) Rect() ([]int, error) {
// |A8(x3,y4) C8(x4,y4)| // |A8(x3,y4) C8(x4,y4)|
// +------------------------+ // +------------------------+
func (f *File) MergeCell(sheet, hCell, vCell string) error { func (f *File) MergeCell(sheet, hCell, vCell string) error {
rect, err := areaRefToCoordinates(hCell + ":" + vCell) rect, err := rangeRefToCoordinates(hCell + ":" + vCell)
if err != nil { if err != nil {
return err 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. // 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") // 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 { func (f *File) UnmergeCell(sheet, hCell, vCell string) error {
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
@ -85,7 +85,7 @@ func (f *File) UnmergeCell(sheet, hCell, vCell string) error {
} }
ws.Lock() ws.Lock()
defer ws.Unlock() defer ws.Unlock()
rect1, err := areaRefToCoordinates(hCell + ":" + vCell) rect1, err := rangeRefToCoordinates(hCell + ":" + vCell)
if err != nil { if err != nil {
return err return err
} }
@ -105,7 +105,7 @@ func (f *File) UnmergeCell(sheet, hCell, vCell string) error {
if mergeCell == nil { if mergeCell == nil {
continue continue
} }
rect2, _ := areaRefToCoordinates(mergeCell.Ref) rect2, _ := rangeRefToCoordinates(mergeCell.Ref)
if isOverlap(rect1, rect2) { if isOverlap(rect1, rect2) {
continue continue
} }

View File

@ -169,7 +169,7 @@ func TestUnmergeCell(t *testing.T) {
f = NewFile() f = NewFile()
assert.NoError(t, f.MergeCell("Sheet1", "A2", "B3")) 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") assert.EqualError(t, f.UnmergeCell("SheetN", "A1", "A1"), "sheet SheetN does not exist")
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml") ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")

View File

@ -279,7 +279,7 @@ var (
// prepareNumberic split the number into two before and after parts by a // prepareNumberic split the number into two before and after parts by a
// decimal point. // decimal point.
func (nf *numberFormat) prepareNumberic(value string) { func (nf *numberFormat) prepareNumberic(value string) {
if nf.isNumeric, _ = isNumeric(value); !nf.isNumeric { if nf.isNumeric, _, _ = isNumeric(value); !nf.isNumeric {
return return
} }
} }
@ -338,13 +338,13 @@ func (nf *numberFormat) positiveHandler() (result string) {
continue continue
} }
if token.TType == nfp.TokenTypeZeroPlaceHolder && token.TValue == strings.Repeat("0", len(token.TValue)) { 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 { if nf.number < 1 {
nf.result += "0" nf.result += "0"
continue continue
} }
if precision > 15 { if precision > 15 {
nf.result += roundPrecision(nf.value, 15) nf.result += strconv.FormatFloat(decimal, 'f', -1, 64)
} else { } else {
nf.result += fmt.Sprintf("%.f", nf.number) nf.result += fmt.Sprintf("%.f", nf.number)
} }
@ -902,13 +902,13 @@ func (nf *numberFormat) negativeHandler() (result string) {
continue continue
} }
if token.TType == nfp.TokenTypeZeroPlaceHolder && token.TValue == strings.Repeat("0", len(token.TValue)) { 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 { if math.Abs(nf.number) < 1 {
nf.result += "0" nf.result += "0"
continue continue
} }
if precision > 15 { if precision > 15 {
nf.result += strings.TrimLeft(roundPrecision(nf.value, 15), "-") nf.result += strings.TrimLeft(strconv.FormatFloat(decimal, 'f', -1, 64), "-")
} else { } else {
nf.result += fmt.Sprintf("%.f", math.Abs(nf.number)) 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 // getValueSectionType returns its applicable number format expression section
// based on the given value. // based on the given value.
func (nf *numberFormat) getValueSectionType(value string) (float64, string) { func (nf *numberFormat) getValueSectionType(value string) (float64, string) {
isNum, _ := isNumeric(value) isNum, _, _ := isNumeric(value)
if !isNum { if !isNum {
return 0, nfp.TokenSectionText return 0, nfp.TokenSectionText
} }

View File

@ -652,11 +652,11 @@ func (f *File) drawingResize(sheet, cell string, width, height float64, formatSe
if inMergeCell { if inMergeCell {
continue continue
} }
if inMergeCell, err = f.checkCellInArea(cell, mergeCell[0]); err != nil { if inMergeCell, err = f.checkCellInRangeRef(cell, mergeCell[0]); err != nil {
return return
} }
if inMergeCell { if inMergeCell {
rng, _ = areaRangeToCoordinates(mergeCell.GetStartAxis(), mergeCell.GetEndAxis()) rng, _ = cellRefsToCoordinates(mergeCell.GetStartAxis(), mergeCell.GetEndAxis())
_ = sortCoordinates(rng) _ = sortCoordinates(rng)
} }
} }

View File

@ -81,8 +81,9 @@ type PivotTableField struct {
// options. Note that the same fields can not in Columns, Rows and Filter // options. Note that the same fields can not in Columns, Rows and Filter
// fields at the same time. // fields at the same time.
// //
// For example, create a pivot table on the Sheet1!$G$2:$M$34 area with the // For example, create a pivot table on the Sheet1!$G$2:$M$34 range reference
// region Sheet1!$A$1:$E$31 as the data source, summarize by sum for sales: // with the region Sheet1!$A$1:$E$31 as the data source, summarize by sum for
// sales:
// //
// package main // package main
// //
@ -205,7 +206,7 @@ func (f *File) adjustRange(rangeStr string) (string, []int, error) {
return "", []int{}, ErrParameterInvalid return "", []int{}, ErrParameterInvalid
} }
trimRng := strings.ReplaceAll(rng[1], "$", "") trimRng := strings.ReplaceAll(rng[1], "$", "")
coordinates, err := areaRefToCoordinates(trimRng) coordinates, err := rangeRefToCoordinates(trimRng)
if err != nil { if err != nil {
return rng[0], []int{}, err return rng[0], []int{}, err
} }

30
rows.go
View File

@ -19,7 +19,6 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"math" "math"
"math/big"
"os" "os"
"strconv" "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 return f.formattedValue(c.S, c.V, raw), nil
default: default:
if isNum, precision := isNumeric(c.V); isNum && !raw { if isNum, precision, decimal := isNumeric(c.V); isNum && !raw {
if precision == 0 { if precision > 15 {
c.V = roundPrecision(c.V, 15) c.V = strconv.FormatFloat(decimal, 'G', 15, 64)
} else { } else {
c.V = roundPrecision(c.V, -1) c.V = strconv.FormatFloat(decimal, 'f', -1, 64)
} }
} }
return f.formattedValue(c.S, c.V, raw), nil 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 // 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: // 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++ row++
} }
for _, rng := range ws.MergeCells.Cells { for _, rng := range ws.MergeCells.Cells {
coordinates, err := areaRefToCoordinates(rng.Ref) coordinates, err := rangeRefToCoordinates(rng.Ref)
if err != nil { if err != nil {
return err 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++ { for i := 0; i < len(ws.MergeCells.Cells); i++ {
areaData := ws.MergeCells.Cells[i] mergedCells := ws.MergeCells.Cells[i]
coordinates, _ := areaRefToCoordinates(areaData.Ref) coordinates, _ := rangeRefToCoordinates(mergedCells.Ref)
x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3] x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
if y1 == y2 && y1 == row { if y1 == y2 && y1 == row {
from, _ := CoordinatesToCellName(x1, row2) from, _ := CoordinatesToCellName(x1, row2)

View File

@ -995,10 +995,6 @@ func TestNumberFormats(t *testing.T) {
assert.NoError(t, f.Close()) assert.NoError(t, f.Close())
} }
func TestRoundPrecision(t *testing.T) {
assert.Equal(t, "text", roundPrecision("text", 0))
}
func BenchmarkRows(b *testing.B) { func BenchmarkRows(b *testing.B) {
f, _ := OpenFile(filepath.Join("test", "Book1.xlsx")) f, _ := OpenFile(filepath.Join("test", "Book1.xlsx"))
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

View File

@ -39,6 +39,9 @@ import (
// `Sheet1` will be created. // `Sheet1` will be created.
func (f *File) NewSheet(sheet string) int { func (f *File) NewSheet(sheet string) int {
// Check if the worksheet already exists // Check if the worksheet already exists
if trimSheetName(sheet) == "" {
return -1
}
index := f.GetSheetIndex(sheet) index := f.GetSheetIndex(sheet)
if index != -1 { if index != -1 {
return index return index

View File

@ -90,6 +90,8 @@ func TestNewSheet(t *testing.T) {
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestNewSheet.xlsx"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestNewSheet.xlsx")))
// create new worksheet with already exists name // create new worksheet with already exists name
assert.Equal(t, f.GetSheetIndex("Sheet2"), f.NewSheet("Sheet2")) 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) { func TestSetPane(t *testing.T) {

View File

@ -145,7 +145,7 @@ func (sw *StreamWriter) AddTable(hCell, vCell, format string) error {
return err return err
} }
coordinates, err := areaRangeToCoordinates(hCell, vCell) coordinates, err := cellRefsToCoordinates(hCell, vCell)
if err != nil { if err != nil {
return err 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. // 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 { if err != nil {
return err 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 // the StreamWriter. Don't create a merged cell that overlaps with another
// existing merged cell. // existing merged cell.
func (sw *StreamWriter) MergeCell(hCell, vCell string) error { func (sw *StreamWriter) MergeCell(hCell, vCell string) error {
_, err := areaRangeToCoordinates(hCell, vCell) _, err := cellRefsToCoordinates(hCell, vCell)
if err != nil { if err != nil {
return err return err
} }

View File

@ -2486,11 +2486,14 @@ func (f *File) GetCellStyle(sheet, cell string) (int, error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
c, col, row, err := f.prepareCell(ws, cell) col, row, err := CellNameToCoordinates(cell)
if err != nil { if err != nil {
return 0, err 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 // 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. // max_color - Same as min_color, see above.
// //
// bar_color - Used for data_bar. 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 var format []*formatConditional
err := json.Unmarshal([]byte(formatSet), &format) err := json.Unmarshal([]byte(formatSet), &format)
if err != nil { if err != nil {
@ -2897,7 +2900,7 @@ func (f *File) SetConditionalFormat(sheet, area, formatSet string) error {
} }
ws.ConditionalFormatting = append(ws.ConditionalFormatting, &xlsxConditionalFormatting{ ws.ConditionalFormatting = append(ws.ConditionalFormatting, &xlsxConditionalFormatting{
SQRef: area, SQRef: reference,
CfRule: cfRule, CfRule: cfRule,
}) })
return err return err

View File

@ -48,7 +48,7 @@ func parseFormatTableSet(formatSet string) (*formatTable, error) {
// Note that the table must be at least two lines including the header. The // 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 cells must contain strings and must be unique, and must set the
// header row data of the table before calling the AddTable function. Multiple // 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 // 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. // 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 { if err != nil {
return err return err
} }