- Support text comparison in the formula, also ref #65
- `GetCellValue`, `GetRows`, `GetCols`, `Rows` and `Cols` support to specify read cell with raw value, ref #621
- Add missing properties for the cell formula
- Update the unit test for the `CalcCellValue`
pull/2/head
xuri 3 years ago
parent 2616aa88cb
commit 32b23ef42d
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7

@ -777,57 +777,25 @@ func calcNEq(rOpd, lOpd string, opdStack *Stack) error {
// calcL evaluate less than arithmetic operations. // calcL evaluate less than arithmetic operations.
func calcL(rOpd, lOpd string, opdStack *Stack) error { func calcL(rOpd, lOpd string, opdStack *Stack) error {
lOpdVal, err := strconv.ParseFloat(lOpd, 64) opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd, rOpd) == -1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
if err != nil {
return err
}
rOpdVal, err := strconv.ParseFloat(rOpd, 64)
if err != nil {
return err
}
opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal > lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
return nil return nil
} }
// calcLe evaluate less than or equal arithmetic operations. // calcLe evaluate less than or equal arithmetic operations.
func calcLe(rOpd, lOpd string, opdStack *Stack) error { func calcLe(rOpd, lOpd string, opdStack *Stack) error {
lOpdVal, err := strconv.ParseFloat(lOpd, 64) opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd, rOpd) != 1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
if err != nil {
return err
}
rOpdVal, err := strconv.ParseFloat(rOpd, 64)
if err != nil {
return err
}
opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal >= lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
return nil return nil
} }
// calcG evaluate greater than or equal arithmetic operations. // calcG evaluate greater than or equal arithmetic operations.
func calcG(rOpd, lOpd string, opdStack *Stack) error { func calcG(rOpd, lOpd string, opdStack *Stack) error {
lOpdVal, err := strconv.ParseFloat(lOpd, 64) opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd, rOpd) == 1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
if err != nil {
return err
}
rOpdVal, err := strconv.ParseFloat(rOpd, 64)
if err != nil {
return err
}
opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal < lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
return nil return nil
} }
// calcGe evaluate greater than or equal arithmetic operations. // calcGe evaluate greater than or equal arithmetic operations.
func calcGe(rOpd, lOpd string, opdStack *Stack) error { func calcGe(rOpd, lOpd string, opdStack *Stack) error {
lOpdVal, err := strconv.ParseFloat(lOpd, 64) opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(strings.Compare(lOpd, rOpd) != -1)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
if err != nil {
return err
}
rOpdVal, err := strconv.ParseFloat(rOpd, 64)
if err != nil {
return err
}
opdStack.Push(efp.Token{TValue: strings.ToUpper(strconv.FormatBool(rOpdVal <= lOpdVal)), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
return nil return nil
} }
@ -1214,7 +1182,7 @@ func (f *File) rangeResolver(cellRefs, cellRanges *list.List) (arg formulaArg, e
if cell, err = CoordinatesToCellName(col, row); err != nil { if cell, err = CoordinatesToCellName(col, row); err != nil {
return return
} }
if value, err = f.GetCellValue(sheet, cell); err != nil { if value, err = f.GetCellValue(sheet, cell, Options{RawCellValue: true}); err != nil {
return return
} }
matrixRow = append(matrixRow, formulaArg{ matrixRow = append(matrixRow, formulaArg{
@ -1233,7 +1201,7 @@ func (f *File) rangeResolver(cellRefs, cellRanges *list.List) (arg formulaArg, e
if cell, err = CoordinatesToCellName(cr.Col, cr.Row); err != nil { if cell, err = CoordinatesToCellName(cr.Col, cr.Row); err != nil {
return return
} }
if arg.String, err = f.GetCellValue(cr.Sheet, cell); err != nil { if arg.String, err = f.GetCellValue(cr.Sheet, cell, Options{RawCellValue: true}); err != nil {
return return
} }
arg.Type = ArgString arg.Type = ArgString
@ -7749,28 +7717,33 @@ func hlookupBinarySearch(row []formulaArg, lookupValue formulaArg) (matchIdx int
return return
} }
// LOOKUP function performs an approximate match lookup in a one-column or // checkLookupArgs checking arguments, prepare lookup value, and data for the
// one-row range, and returns the corresponding value from another one-column // formula function LOOKUP.
// or one-row range. The syntax of the function is: func checkLookupArgs(argsList *list.List) (arrayForm bool, lookupValue, lookupVector, errArg formulaArg) {
//
// LOOKUP(lookup_value,lookup_vector,[result_vector])
//
func (fn *formulaFuncs) LOOKUP(argsList *list.List) formulaArg {
if argsList.Len() < 2 { if argsList.Len() < 2 {
return newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires at least 2 arguments") errArg = newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires at least 2 arguments")
return
} }
if argsList.Len() > 3 { if argsList.Len() > 3 {
return newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires at most 3 arguments") errArg = newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires at most 3 arguments")
return
} }
lookupValue := argsList.Front().Value.(formulaArg) lookupValue = argsList.Front().Value.(formulaArg)
lookupVector := argsList.Front().Next().Value.(formulaArg) lookupVector = argsList.Front().Next().Value.(formulaArg)
if lookupVector.Type != ArgMatrix && lookupVector.Type != ArgList { if lookupVector.Type != ArgMatrix && lookupVector.Type != ArgList {
return newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires second argument of table array") errArg = newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires second argument of table array")
return
} }
arrayForm := lookupVector.Type == ArgMatrix arrayForm = lookupVector.Type == ArgMatrix
if arrayForm && len(lookupVector.Matrix) == 0 { if arrayForm && len(lookupVector.Matrix) == 0 {
return newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires not empty range as second argument") errArg = newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires not empty range as second argument")
} }
return
}
// iterateLookupArgs iterate arguments to extract columns and calculate match
// index for the formula function LOOKUP.
func iterateLookupArgs(lookupValue, lookupVector formulaArg) ([]formulaArg, int, bool) {
cols, matchIdx, ok := lookupCol(lookupVector, 0), -1, false cols, matchIdx, ok := lookupCol(lookupVector, 0), -1, false
for idx, col := range cols { for idx, col := range cols {
lhs := lookupValue lhs := lookupValue
@ -7796,6 +7769,21 @@ func (fn *formulaFuncs) LOOKUP(argsList *list.List) formulaArg {
matchIdx = idx - 1 matchIdx = idx - 1
} }
} }
return cols, matchIdx, ok
}
// LOOKUP function performs an approximate match lookup in a one-column or
// one-row range, and returns the corresponding value from another one-column
// or one-row range. The syntax of the function is:
//
// LOOKUP(lookup_value,lookup_vector,[result_vector])
//
func (fn *formulaFuncs) LOOKUP(argsList *list.List) formulaArg {
arrayForm, lookupValue, lookupVector, errArg := checkLookupArgs(argsList)
if errArg.Type == ArgError {
return errArg
}
cols, matchIdx, ok := iterateLookupArgs(lookupValue, lookupVector)
if ok && matchIdx == -1 { if ok && matchIdx == -1 {
matchIdx = len(cols) - 1 matchIdx = len(cols) - 1
} }

@ -2452,17 +2452,27 @@ func TestCalcWithDefinedName(t *testing.T) {
} }
func TestCalcArithmeticOperations(t *testing.T) { func TestCalcArithmeticOperations(t *testing.T) {
opdStack := NewStack()
for _, test := range [][]string{{"1", "text", "FALSE"}, {"text", "1", "TRUE"}} {
assert.NoError(t, calcL(test[0], test[1], opdStack))
assert.Equal(t, test[2], opdStack.Peek().(efp.Token).TValue)
opdStack.Empty()
assert.NoError(t, calcLe(test[0], test[1], opdStack))
assert.Equal(t, test[2], opdStack.Peek().(efp.Token).TValue)
opdStack.Empty()
}
for _, test := range [][]string{{"1", "text", "TRUE"}, {"text", "1", "FALSE"}} {
assert.NoError(t, calcG(test[0], test[1], opdStack))
assert.Equal(t, test[2], opdStack.Peek().(efp.Token).TValue)
opdStack.Empty()
assert.NoError(t, calcGe(test[0], test[1], opdStack))
assert.Equal(t, test[2], opdStack.Peek().(efp.Token).TValue)
opdStack.Empty()
}
err := `strconv.ParseFloat: parsing "text": invalid syntax` err := `strconv.ParseFloat: parsing "text": invalid syntax`
assert.EqualError(t, calcPow("1", "text", nil), err) assert.EqualError(t, calcPow("1", "text", nil), err)
assert.EqualError(t, calcPow("text", "1", nil), err) assert.EqualError(t, calcPow("text", "1", nil), err)
assert.EqualError(t, calcL("1", "text", nil), err)
assert.EqualError(t, calcL("text", "1", nil), err)
assert.EqualError(t, calcLe("1", "text", nil), err)
assert.EqualError(t, calcLe("text", "1", nil), err)
assert.EqualError(t, calcG("1", "text", nil), err)
assert.EqualError(t, calcG("text", "1", nil), err)
assert.EqualError(t, calcGe("1", "text", nil), err)
assert.EqualError(t, calcGe("text", "1", nil), err)
assert.EqualError(t, calcAdd("1", "text", nil), err) assert.EqualError(t, calcAdd("1", "text", nil), err)
assert.EqualError(t, calcAdd("text", "1", nil), err) assert.EqualError(t, calcAdd("text", "1", nil), err)
assert.EqualError(t, calcAdd("1", "text", nil), err) assert.EqualError(t, calcAdd("1", "text", nil), err)

@ -36,9 +36,9 @@ const (
// format to the cell value, it will do so, if not then an error will be // format to the cell value, it will do so, if not then an error will be
// returned, along with the raw value of the cell. All cells' values will be // returned, along with the raw value of the cell. All cells' values will be
// the same in a merged range. // the same in a merged range.
func (f *File) GetCellValue(sheet, axis string) (string, error) { func (f *File) GetCellValue(sheet, axis string, opts ...Options) (string, error) {
return f.getCellStringFunc(sheet, axis, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) { return f.getCellStringFunc(sheet, axis, func(x *xlsxWorksheet, c *xlsxC) (string, bool, error) {
val, err := c.getValueFrom(f, f.sharedStringsReader()) val, err := c.getValueFrom(f, f.sharedStringsReader(), parseOptions(opts...).RawCellValue)
return val, true, err return val, true, err
}) })
} }
@ -440,7 +440,6 @@ type FormulaOpts struct {
// err := f.SetCellFormula("Sheet1", "A3", "=A1:A2", // err := f.SetCellFormula("Sheet1", "A3", "=A1:A2",
// excelize.FormulaOpts{Ref: &ref, Type: &formulaType}) // excelize.FormulaOpts{Ref: &ref, Type: &formulaType})
// //
//
// Example 6, set shared formula "=A1+B1" for the cell "C1:C5" // Example 6, set shared formula "=A1+B1" for the cell "C1:C5"
// on "Sheet1", "C1" is the master cell: // on "Sheet1", "C1" is the master cell:
// //
@ -448,6 +447,41 @@ type FormulaOpts struct {
// err := f.SetCellFormula("Sheet1", "C1", "=A1+B1", // err := f.SetCellFormula("Sheet1", "C1", "=A1+B1",
// excelize.FormulaOpts{Ref: &ref, Type: &formulaType}) // excelize.FormulaOpts{Ref: &ref, Type: &formulaType})
// //
// Example 7, set table formula "=SUM(Table1[[A]:[B]])" for the cell "C2"
// on "Sheet1":
//
// package main
//
// import (
// "fmt"
//
// "github.com/xuri/excelize/v2"
// )
//
// func main() {
// f := excelize.NewFile()
// for idx, row := range [][]interface{}{{"A", "B", "C"}, {1, 2}} {
// if err := f.SetSheetRow("Sheet1", fmt.Sprintf("A%d", idx+1), &row); err != nil {
// fmt.Println(err)
// return
// }
// }
// if err := f.AddTable("Sheet1", "A1", "C2",
// `{"table_name":"Table1","table_style":"TableStyleMedium2"}`); err != nil {
// fmt.Println(err)
// return
// }
// formulaType := excelize.STCellFormulaTypeDataTable
// if err := f.SetCellFormula("Sheet1", "C2", "=SUM(Table1[[A]:[B]])",
// excelize.FormulaOpts{Type: &formulaType}); err != nil {
// fmt.Println(err)
// return
// }
// if err := f.SaveAs("Book1.xlsx"); err != nil {
// fmt.Println(err)
// }
// }
//
func (f *File) SetCellFormula(sheet, axis, formula string, opts ...FormulaOpts) error { func (f *File) SetCellFormula(sheet, axis, formula string, opts ...FormulaOpts) error {
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
@ -471,6 +505,9 @@ func (f *File) SetCellFormula(sheet, axis, formula string, opts ...FormulaOpts)
for _, o := range opts { for _, o := range opts {
if o.Type != nil { if o.Type != nil {
if *o.Type == STCellFormulaTypeDataTable {
return err
}
cellData.F.T = *o.Type cellData.F.T = *o.Type
if cellData.F.T == STCellFormulaTypeShared { if cellData.F.T == STCellFormulaTypeShared {
if err = ws.setSharedFormula(*o.Ref); err != nil { if err = ws.setSharedFormula(*o.Ref); err != nil {
@ -955,8 +992,8 @@ func (f *File) getCellStringFunc(sheet, axis string, fn func(x *xlsxWorksheet, c
// formattedValue provides a function to returns a value after formatted. If // formattedValue provides a function to returns a value after formatted. If
// it is possible to apply a format to the cell value, it will do so, if not // it is possible to apply a format to the cell value, it will do so, if not
// then an error will be returned, along with the raw value of the cell. // then an error will be returned, along with the raw value of the cell.
func (f *File) formattedValue(s int, v string) string { func (f *File) formattedValue(s int, v string, raw bool) string {
if s == 0 { if s == 0 || raw {
return v return v
} }
styleSheet := f.stylesReader() styleSheet := f.stylesReader()

@ -356,6 +356,16 @@ func TestSetCellFormula(t *testing.T) {
ref = "" ref = ""
assert.EqualError(t, f.SetCellFormula("Sheet1", "D1", "=A1+C1", FormulaOpts{Ref: &ref, Type: &formulaType}), ErrParameterInvalid.Error()) assert.EqualError(t, f.SetCellFormula("Sheet1", "D1", "=A1+C1", FormulaOpts{Ref: &ref, Type: &formulaType}), ErrParameterInvalid.Error())
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellFormula5.xlsx"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellFormula5.xlsx")))
// Test set table formula for the cells.
f = NewFile()
for idx, row := range [][]interface{}{{"A", "B", "C"}, {1, 2}} {
assert.NoError(t, f.SetSheetRow("Sheet1", fmt.Sprintf("A%d", idx+1), &row))
}
assert.NoError(t, f.AddTable("Sheet1", "A1", "C2", `{"table_name":"Table1","table_style":"TableStyleMedium2"}`))
formulaType = STCellFormulaTypeDataTable
assert.NoError(t, f.SetCellFormula("Sheet1", "C2", "=SUM(Table1[[A]:[B]])", FormulaOpts{Type: &formulaType}))
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellFormula6.xlsx")))
} }
func TestGetCellRichText(t *testing.T) { func TestGetCellRichText(t *testing.T) {
@ -503,20 +513,20 @@ func TestSetCellRichText(t *testing.T) {
func TestFormattedValue2(t *testing.T) { func TestFormattedValue2(t *testing.T) {
f := NewFile() f := NewFile()
v := f.formattedValue(0, "43528") v := f.formattedValue(0, "43528", false)
assert.Equal(t, "43528", v) assert.Equal(t, "43528", v)
v = f.formattedValue(15, "43528") v = f.formattedValue(15, "43528", false)
assert.Equal(t, "43528", v) assert.Equal(t, "43528", v)
v = f.formattedValue(1, "43528") v = f.formattedValue(1, "43528", false)
assert.Equal(t, "43528", v) assert.Equal(t, "43528", v)
customNumFmt := "[$-409]MM/DD/YYYY" customNumFmt := "[$-409]MM/DD/YYYY"
_, err := f.NewStyle(&Style{ _, err := f.NewStyle(&Style{
CustomNumFmt: &customNumFmt, CustomNumFmt: &customNumFmt,
}) })
assert.NoError(t, err) assert.NoError(t, err)
v = f.formattedValue(1, "43528") v = f.formattedValue(1, "43528", false)
assert.Equal(t, "03/04/2019", v) assert.Equal(t, "03/04/2019", v)
// formatted value with no built-in number format ID // formatted value with no built-in number format ID
@ -524,20 +534,20 @@ func TestFormattedValue2(t *testing.T) {
f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{ f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{
NumFmtID: &numFmtID, NumFmtID: &numFmtID,
}) })
v = f.formattedValue(2, "43528") v = f.formattedValue(2, "43528", false)
assert.Equal(t, "43528", v) assert.Equal(t, "43528", v)
// formatted value with invalid number format ID // formatted value with invalid number format ID
f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{ f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{
NumFmtID: nil, NumFmtID: nil,
}) })
_ = f.formattedValue(3, "43528") _ = f.formattedValue(3, "43528", false)
// formatted value with empty number format // formatted value with empty number format
f.Styles.NumFmts = nil f.Styles.NumFmts = nil
f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{ f.Styles.CellXfs.Xf = append(f.Styles.CellXfs.Xf, xlsxXf{
NumFmtID: &numFmtID, NumFmtID: &numFmtID,
}) })
v = f.formattedValue(1, "43528") v = f.formattedValue(1, "43528", false)
assert.Equal(t, "43528", v) assert.Equal(t, "43528", v)
} }

@ -34,6 +34,7 @@ const (
type Cols struct { type Cols struct {
err error err error
curCol, totalCol, stashCol, totalRow int curCol, totalCol, stashCol, totalRow int
rawCellValue bool
sheet string sheet string
f *File f *File
sheetXML []byte sheetXML []byte
@ -54,14 +55,14 @@ type Cols struct {
// fmt.Println() // fmt.Println()
// } // }
// //
func (f *File) GetCols(sheet string) ([][]string, error) { func (f *File) GetCols(sheet string, opts ...Options) ([][]string, error) {
cols, err := f.Cols(sheet) cols, err := f.Cols(sheet)
if err != nil { if err != nil {
return nil, err return nil, err
} }
results := make([][]string, 0, 64) results := make([][]string, 0, 64)
for cols.Next() { for cols.Next() {
col, _ := cols.Rows() col, _ := cols.Rows(opts...)
results = append(results, col) results = append(results, col)
} }
return results, nil return results, nil
@ -79,7 +80,7 @@ func (cols *Cols) Error() error {
} }
// Rows return the current column's row values. // Rows return the current column's row values.
func (cols *Cols) Rows() ([]string, error) { func (cols *Cols) Rows(opts ...Options) ([]string, error) {
var ( var (
err error err error
inElement string inElement string
@ -89,6 +90,7 @@ func (cols *Cols) Rows() ([]string, error) {
if cols.stashCol >= cols.curCol { if cols.stashCol >= cols.curCol {
return rows, err return rows, err
} }
cols.rawCellValue = parseOptions(opts...).RawCellValue
d := cols.f.sharedStringsReader() d := cols.f.sharedStringsReader()
decoder := cols.f.xmlNewDecoder(bytes.NewReader(cols.sheetXML)) decoder := cols.f.xmlNewDecoder(bytes.NewReader(cols.sheetXML))
for { for {
@ -123,7 +125,7 @@ func (cols *Cols) Rows() ([]string, error) {
if cellCol == cols.curCol { if cellCol == cols.curCol {
colCell := xlsxC{} colCell := xlsxC{}
_ = decoder.DecodeElement(&colCell, &xmlElement) _ = decoder.DecodeElement(&colCell, &xmlElement)
val, _ := colCell.getValueFrom(cols.f, d) val, _ := colCell.getValueFrom(cols.f, d, cols.rawCellValue)
rows = append(rows, val) rows = append(rows, val)
} }
} }

@ -58,9 +58,12 @@ type File struct {
type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, err error) type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, err error)
// Options define the options for open spreadsheet. // Options define the options for open and reading spreadsheet. RawCellValue
// specify if apply the number format for the cell value or get the raw
// value.
type Options struct { type Options struct {
Password string Password string
RawCellValue bool
UnzipSizeLimit int64 UnzipSizeLimit int64
} }
@ -119,11 +122,9 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) {
return nil, err return nil, err
} }
f := newFile() f := newFile()
for i := range opt { f.options = parseOptions(opt...)
f.options = &opt[i] if f.options.UnzipSizeLimit == 0 {
if f.options.UnzipSizeLimit == 0 { f.options.UnzipSizeLimit = UnzipSizeLimit
f.options.UnzipSizeLimit = UnzipSizeLimit
}
} }
if bytes.Contains(b, oleIdentifier) { if bytes.Contains(b, oleIdentifier) {
b, err = Decrypt(b, f.options) b, err = Decrypt(b, f.options)
@ -150,6 +151,16 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) {
return f, nil return f, nil
} }
// parseOptions provides a function to parse the optional settings for open
// and reading spreadsheet.
func parseOptions(opts ...Options) *Options {
opt := &Options{}
for _, o := range opts {
opt = &o
}
return opt
}
// CharsetTranscoder Set user defined codepage transcoder function for open // CharsetTranscoder Set user defined codepage transcoder function for open
// XLSX from non UTF-8 encoding. // XLSX from non UTF-8 encoding.
func (f *File) CharsetTranscoder(fn charsetTranscoderFn) *File { f.CharsetReader = fn; return f } func (f *File) CharsetTranscoder(fn charsetTranscoderFn) *File { f.CharsetReader = fn; return f }

@ -43,7 +43,7 @@ import (
// fmt.Println() // fmt.Println()
// } // }
// //
func (f *File) GetRows(sheet string) ([][]string, error) { func (f *File) GetRows(sheet string, opts ...Options) ([][]string, error) {
rows, err := f.Rows(sheet) rows, err := f.Rows(sheet)
if err != nil { if err != nil {
return nil, err return nil, err
@ -51,7 +51,7 @@ func (f *File) GetRows(sheet string) ([][]string, error) {
results, cur, max := make([][]string, 0, 64), 0, 0 results, cur, max := make([][]string, 0, 64), 0, 0
for rows.Next() { for rows.Next() {
cur++ cur++
row, err := rows.Columns() row, err := rows.Columns(opts...)
if err != nil { if err != nil {
break break
} }
@ -67,6 +67,7 @@ func (f *File) GetRows(sheet string) ([][]string, error) {
type Rows struct { type Rows struct {
err error err error
curRow, totalRow, stashRow int curRow, totalRow, stashRow int
rawCellValue bool
sheet string sheet string
f *File f *File
decoder *xml.Decoder decoder *xml.Decoder
@ -84,11 +85,12 @@ func (rows *Rows) Error() error {
} }
// Columns return the current row's column values. // Columns return the current row's column values.
func (rows *Rows) Columns() ([]string, error) { func (rows *Rows) Columns(opts ...Options) ([]string, error) {
var rowIterator rowXMLIterator var rowIterator rowXMLIterator
if rows.stashRow >= rows.curRow { if rows.stashRow >= rows.curRow {
return rowIterator.columns, rowIterator.err return rowIterator.columns, rowIterator.err
} }
rows.rawCellValue = parseOptions(opts...).RawCellValue
rowIterator.rows = rows rowIterator.rows = rows
rowIterator.d = rows.f.sharedStringsReader() rowIterator.d = rows.f.sharedStringsReader()
for { for {
@ -109,7 +111,7 @@ func (rows *Rows) Columns() ([]string, error) {
return rowIterator.columns, rowIterator.err return rowIterator.columns, rowIterator.err
} }
} }
rowXMLHandler(&rowIterator, &xmlElement) rowXMLHandler(&rowIterator, &xmlElement, rows.rawCellValue)
if rowIterator.err != nil { if rowIterator.err != nil {
return rowIterator.columns, rowIterator.err return rowIterator.columns, rowIterator.err
} }
@ -157,7 +159,7 @@ type rowXMLIterator struct {
} }
// rowXMLHandler parse the row XML element of the worksheet. // rowXMLHandler parse the row XML element of the worksheet.
func rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.StartElement) { func rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.StartElement, raw bool) {
rowIterator.err = nil rowIterator.err = nil
if rowIterator.inElement == "c" { if rowIterator.inElement == "c" {
rowIterator.cellCol++ rowIterator.cellCol++
@ -169,7 +171,7 @@ func rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.StartElement) {
} }
} }
blank := rowIterator.cellCol - len(rowIterator.columns) blank := rowIterator.cellCol - len(rowIterator.columns)
val, _ := colCell.getValueFrom(rowIterator.rows.f, rowIterator.d) val, _ := colCell.getValueFrom(rowIterator.rows.f, rowIterator.d, raw)
if val != "" || colCell.F != nil { if val != "" || colCell.F != nil {
rowIterator.columns = append(appendSpace(blank, rowIterator.columns), val) rowIterator.columns = append(appendSpace(blank, rowIterator.columns), val)
} }
@ -361,7 +363,7 @@ func (f *File) sharedStringsReader() *xlsxSST {
// getValueFrom return a value from a column/row cell, this function is // getValueFrom return a value from a column/row cell, this function is
// inteded to be used with for range on rows an argument with the spreadsheet // inteded to be used with for range on rows an argument with the spreadsheet
// opened file. // opened file.
func (c *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) { func (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (string, error) {
f.Lock() f.Lock()
defer f.Unlock() defer f.Unlock()
switch c.T { switch c.T {
@ -370,26 +372,26 @@ func (c *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
xlsxSI := 0 xlsxSI := 0
xlsxSI, _ = strconv.Atoi(c.V) xlsxSI, _ = strconv.Atoi(c.V)
if len(d.SI) > xlsxSI { if len(d.SI) > xlsxSI {
return f.formattedValue(c.S, d.SI[xlsxSI].String()), nil return f.formattedValue(c.S, d.SI[xlsxSI].String(), raw), nil
} }
} }
return f.formattedValue(c.S, c.V), nil return f.formattedValue(c.S, c.V, raw), nil
case "str": case "str":
return f.formattedValue(c.S, c.V), nil return f.formattedValue(c.S, c.V, raw), nil
case "inlineStr": case "inlineStr":
if c.IS != nil { if c.IS != nil {
return f.formattedValue(c.S, c.IS.String()), nil return f.formattedValue(c.S, c.IS.String(), raw), nil
} }
return f.formattedValue(c.S, c.V), nil return f.formattedValue(c.S, c.V, raw), nil
default: default:
isNum, precision := isNumeric(c.V) isNum, precision := isNumeric(c.V)
if isNum && precision > 10 { if isNum && precision > 10 {
val, _ := roundPrecision(c.V) val, _ := roundPrecision(c.V)
if val != c.V { if val != c.V {
return f.formattedValue(c.S, val), nil return f.formattedValue(c.S, val, raw), nil
} }
} }
return f.formattedValue(c.S, c.V), nil return f.formattedValue(c.S, c.V, raw), nil
} }
} }

@ -845,7 +845,7 @@ func TestGetValueFromInlineStr(t *testing.T) {
c := &xlsxC{T: "inlineStr"} c := &xlsxC{T: "inlineStr"}
f := NewFile() f := NewFile()
d := &xlsxSST{} d := &xlsxSST{}
val, err := c.getValueFrom(f, d) val, err := c.getValueFrom(f, d, false)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "", val) assert.Equal(t, "", val)
} }
@ -865,7 +865,7 @@ func TestGetValueFromNumber(t *testing.T) {
"2.220000ddsf0000000002-r": "2.220000ddsf0000000002-r", "2.220000ddsf0000000002-r": "2.220000ddsf0000000002-r",
} { } {
c.V = input c.V = input
val, err := c.getValueFrom(f, d) val, err := c.getValueFrom(f, d, false)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, expected, val) assert.Equal(t, expected, val)
} }

@ -880,7 +880,7 @@ func (f *File) searchSheet(name, value string, regSearch bool) (result []string,
if inElement == "c" { if inElement == "c" {
colCell := xlsxC{} colCell := xlsxC{}
_ = decoder.DecodeElement(&colCell, &xmlElement) _ = decoder.DecodeElement(&colCell, &xmlElement)
val, _ := colCell.getValueFrom(f, d) val, _ := colCell.getValueFrom(f, d, false)
if regSearch { if regSearch {
regex := regexp.MustCompile(value) regex := regexp.MustCompile(value)
if !regex.MatchString(val) { if !regex.MatchString(val) {

@ -473,9 +473,18 @@ type xlsxC struct {
// contained in the character node of this element. // contained in the character node of this element.
type xlsxF struct { type xlsxF struct {
Content string `xml:",chardata"` Content string `xml:",chardata"`
T string `xml:"t,attr,omitempty"` // Formula type T string `xml:"t,attr,omitempty"` // Formula type
Aca bool `xml:"aca,attr,omitempty"`
Ref string `xml:"ref,attr,omitempty"` // Shared formula ref Ref string `xml:"ref,attr,omitempty"` // Shared formula ref
Si *int `xml:"si,attr"` // Shared formula index Dt2D bool `xml:"dt2D,attr,omitempty"`
Dtr bool `xml:"dtr,attr,omitempty"`
Del1 bool `xml:"del1,attr,omitempty"`
Del2 bool `xml:"del2,attr,omitempty"`
R1 string `xml:"r1,attr,omitempty"`
R2 string `xml:"r2,attr,omitempty"`
Ca bool `xml:"ca,attr,omitempty"`
Si *int `xml:"si,attr"` // Shared formula index
Bx bool `xml:"bx,attr,omitempty"`
} }
// xlsxSheetProtection collection expresses the sheet protection options to // xlsxSheetProtection collection expresses the sheet protection options to

Loading…
Cancel
Save