This closes #1955, refs #119, support to set cell value with an IEEE 754 "not-a-number" value or infinity

This commit is contained in:
xuri 2024-07-18 21:05:36 +08:00
parent 68a1704900
commit 4dd34477f7
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
4 changed files with 31 additions and 8 deletions

15
cell.go
View File

@ -15,6 +15,7 @@ import (
"bytes" "bytes"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"math"
"os" "os"
"reflect" "reflect"
"strconv" "strconv"
@ -385,6 +386,9 @@ func setCellBool(value bool) (t string, v string) {
// var x float32 = 1.325 // var x float32 = 1.325
// f.SetCellFloat("Sheet1", "A1", float64(x), 2, 32) // f.SetCellFloat("Sheet1", "A1", float64(x), 2, 32)
func (f *File) SetCellFloat(sheet, cell string, value float64, precision, bitSize int) error { func (f *File) SetCellFloat(sheet, cell string, value float64, precision, bitSize int) error {
if math.IsNaN(value) || math.IsInf(value, 0) {
return f.SetCellStr(sheet, cell, fmt.Sprint(value))
}
f.mu.Lock() f.mu.Lock()
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
@ -399,17 +403,20 @@ func (f *File) SetCellFloat(sheet, cell string, value float64, precision, bitSiz
return err return err
} }
c.S = ws.prepareCellStyle(col, row, c.S) c.S = ws.prepareCellStyle(col, row, c.S)
c.T, c.V = setCellFloat(value, precision, bitSize) c.setCellFloat(value, precision, bitSize)
c.IS = nil
return f.removeFormula(c, ws, sheet) return f.removeFormula(c, ws, sheet)
} }
// setCellFloat prepares cell type and string type cell value by a given float // setCellFloat prepares cell type and string type cell value by a given float
// value. // value.
func setCellFloat(value float64, precision, bitSize int) (t string, v string) { func (c *xlsxC) setCellFloat(value float64, precision, bitSize int) {
v = strconv.FormatFloat(value, 'f', precision, bitSize) if math.IsNaN(value) || math.IsInf(value, 0) {
c.setInlineStr(fmt.Sprint(value))
return return
} }
c.T, c.V = "", strconv.FormatFloat(value, 'f', precision, bitSize)
c.IS = nil
}
// SetCellStr provides a function to set string type value of a cell. Total // SetCellStr provides a function to set string type value of a cell. Total
// number of characters that a cell can contain 32767 characters. // number of characters that a cell can contain 32767 characters.

View File

@ -292,6 +292,19 @@ func TestSetCellValue(t *testing.T) {
val, err = f.GetCellValue("Sheet1", "B1") val, err = f.GetCellValue("Sheet1", "B1")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "b", val) assert.Equal(t, "b", val)
f = NewFile()
// Test set cell value with an IEEE 754 "not-a-number" value or infinity
for num, expected := range map[float64]string{
math.NaN(): "NaN",
math.Inf(0): "+Inf",
math.Inf(-1): "-Inf",
} {
assert.NoError(t, f.SetCellValue("Sheet1", "A1", num))
val, err := f.GetCellValue("Sheet1", "A1")
assert.NoError(t, err)
assert.Equal(t, expected, val)
}
} }
func TestSetCellValues(t *testing.T) { func TestSetCellValues(t *testing.T) {

View File

@ -529,9 +529,9 @@ func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) error {
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
setCellIntFunc(c, val) setCellIntFunc(c, val)
case float32: case float32:
c.T, c.V = setCellFloat(float64(val), -1, 32) c.setCellFloat(float64(val), -1, 32)
case float64: case float64:
c.T, c.V = setCellFloat(val, -1, 64) c.setCellFloat(val, -1, 64)
case string: case string:
c.setCellValue(val) c.setCellValue(val)
case []byte: case []byte:

View File

@ -4,6 +4,7 @@ import (
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"io" "io"
"math"
"math/rand" "math/rand"
"os" "os"
"path/filepath" "path/filepath"
@ -76,6 +77,8 @@ func TestStreamWriter(t *testing.T) {
assert.NoError(t, streamWriter.SetRow("A7", nil, RowOpts{Height: 20, Hidden: true, StyleID: styleID})) assert.NoError(t, streamWriter.SetRow("A7", nil, RowOpts{Height: 20, Hidden: true, StyleID: styleID}))
assert.Equal(t, ErrMaxRowHeight, streamWriter.SetRow("A8", nil, RowOpts{Height: MaxRowHeight + 1})) assert.Equal(t, ErrMaxRowHeight, streamWriter.SetRow("A8", nil, RowOpts{Height: MaxRowHeight + 1}))
assert.NoError(t, streamWriter.SetRow("A9", []interface{}{math.NaN(), math.Inf(0), math.Inf(-1)}))
for rowID := 10; rowID <= 51200; rowID++ { for rowID := 10; rowID <= 51200; rowID++ {
row := make([]interface{}, 50) row := make([]interface{}, 50)
for colID := 0; colID < 50; colID++ { for colID := 0; colID < 50; colID++ {
@ -145,7 +148,7 @@ func TestStreamWriter(t *testing.T) {
cells += len(row) cells += len(row)
} }
assert.NoError(t, rows.Close()) assert.NoError(t, rows.Close())
assert.Equal(t, 2559559, cells) assert.Equal(t, 2559562, cells)
// Save spreadsheet with password. // Save spreadsheet with password.
assert.NoError(t, file.SaveAs(filepath.Join("test", "EncryptionTestStreamWriter.xlsx"), Options{Password: "password"})) assert.NoError(t, file.SaveAs(filepath.Join("test", "EncryptionTestStreamWriter.xlsx"), Options{Password: "password"}))
assert.NoError(t, file.Close()) assert.NoError(t, file.Close())