From b133399759b0e3c8c786a46a0cc7642fab970159 Mon Sep 17 00:00:00 2001 From: Thomas Charbonnel Date: Wed, 3 Aug 2022 16:21:51 +0800 Subject: [PATCH 1/4] #1299 don't write nil values in SetRow --- stream.go | 3 +++ stream_test.go | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/stream.go b/stream.go index 1a1af24412..52e65a46c8 100644 --- a/stream.go +++ b/stream.go @@ -327,6 +327,9 @@ func (sw *StreamWriter) SetRow(axis string, values []interface{}, opts ...RowOpt } fmt.Fprintf(&sw.rawData, ``, row, attrs) for i, val := range values { + if val == nil { + continue + } axis, err := CoordinatesToCellName(col+i, row) if err != nil { return err diff --git a/stream_test.go b/stream_test.go index 6843e2064f..8f6a5b4cf5 100644 --- a/stream_test.go +++ b/stream_test.go @@ -209,6 +209,17 @@ func TestSetRow(t *testing.T) { assert.EqualError(t, streamWriter.SetRow("A", []interface{}{}), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error()) } +func TestSetRowNilValues(t *testing.T) { + file := NewFile() + streamWriter, err := file.NewStreamWriter("Sheet1") + assert.NoError(t, err) + streamWriter.SetRow("A1", []interface{}{nil, nil, Cell{Value: "foo"}}) + streamWriter.Flush() + ws, err := file.workSheetReader("Sheet1") + assert.NoError(t, err) + assert.NotEqual(t, ws.SheetData.Row[0].C[0].XMLName.Local, "c") +} + func TestSetCellValFunc(t *testing.T) { f := NewFile() sw, err := f.NewStreamWriter("Sheet1") From c8e5b9cb7c56b77f9626d7e3a2b32fe6180c1c0b Mon Sep 17 00:00:00 2001 From: Thomas Charbonnel Date: Mon, 1 Aug 2022 16:32:24 +0800 Subject: [PATCH 2/4] #1296 add GetStyleID to Rows Signed-off-by: Thomas Charbonnel --- rows.go | 19 +++++++++++++++++++ rows_test.go | 22 ++++++++++++++++++++++ sheet.go | 28 ++++++++++++++++++++++++++++ test/Book1.xlsx | Bin 20738 -> 20451 bytes xmlDrawing.go | 1 + 5 files changed, 70 insertions(+) diff --git a/rows.go b/rows.go index 853c8f7df3..9ef52cadd4 100644 --- a/rows.go +++ b/rows.go @@ -80,6 +80,7 @@ type Rows struct { sst *xlsxSST decoder *xml.Decoder token xml.Token + rowOpts RowOpts } // Next will return true if find the next row element. @@ -101,6 +102,17 @@ func (rows *Rows) Next() bool { rows.curRow = rowNum } rows.token = token + ro := RowOpts{} + if styleID, err := attrValToInt("s", xmlElement.Attr); err == nil && styleID > 0 && styleID < MaxCellStyles { + ro.StyleID = styleID + } + if hidden, err := attrValToBool("hidden", xmlElement.Attr); err == nil { + ro.Hidden = hidden + } + if height, err := attrValToFloat("ht", xmlElement.Attr); err == nil { + ro.Height = height + } + rows.rowOpts = ro return true } case xml.EndElement: @@ -111,6 +123,13 @@ func (rows *Rows) Next() bool { } } +// GetStyleID will return the RowOpts of the current row. +// +// rowOpts := rows.GetRowOpts() +func (rows *Rows) GetRowOpts() RowOpts { + return rows.rowOpts +} + // Error will return the error when the error occurs. func (rows *Rows) Error() error { return rows.err diff --git a/rows_test.go b/rows_test.go index 4fe28517cd..ec25bd938a 100644 --- a/rows_test.go +++ b/rows_test.go @@ -96,6 +96,28 @@ func TestRowsIterator(t *testing.T) { assert.Equal(t, expectedNumRow, rowCount) } +func TestRowsGetRowOpts(t *testing.T) { + sheetName := "Sheet2" + expectedRowStyleID1 := RowOpts{Height: 17.0, Hidden: false, StyleID: 1} + expectedRowStyleID2 := RowOpts{Height: 17.0, Hidden: false, StyleID: 0} + expectedRowStyleID3 := RowOpts{Height: 17.0, Hidden: false, StyleID: 2} + f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) + require.NoError(t, err) + + rows, err := f.Rows(sheetName) + require.NoError(t, err) + + rows.Next() + got := rows.GetRowOpts() + assert.Equal(t, expectedRowStyleID1, got) + rows.Next() + got = rows.GetRowOpts() + assert.Equal(t, expectedRowStyleID2, got) + rows.Next() + got = rows.GetRowOpts() + assert.Equal(t, expectedRowStyleID3, got) +} + func TestRowsError(t *testing.T) { f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) if !assert.NoError(t, err) { diff --git a/sheet.go b/sheet.go index 01dd1672b9..e984dbd065 100644 --- a/sheet.go +++ b/sheet.go @@ -928,6 +928,34 @@ func attrValToInt(name string, attrs []xml.Attr) (val int, err error) { return } +// attrValToFloat provides a function to convert the local names to a float64 +// by given XML attributes and specified names. +func attrValToFloat(name string, attrs []xml.Attr) (val float64, err error) { + for _, attr := range attrs { + if attr.Name.Local == name { + val, err = strconv.ParseFloat(attr.Value, 64) + if err != nil { + return + } + } + } + return +} + +// attrValToBool provides a function to convert the local names to a boot +// by given XML attributes and specified names. +func attrValToBool(name string, attrs []xml.Attr) (val bool, err error) { + for _, attr := range attrs { + if attr.Name.Local == name { + val, err = strconv.ParseBool(attr.Value) + if err != nil { + return + } + } + } + return +} + // SetHeaderFooter provides a function to set headers and footers by given // worksheet name and the control characters. // diff --git a/test/Book1.xlsx b/test/Book1.xlsx index 6a497e33afb45a5764a323326bd1f2f57480184d..ed3e29295461c903ad4511cc7d434b18297b293f 100644 GIT binary patch delta 2144 zcmaJ>YgAKL7QQzm*1jaWA^0;6~1r(4ef`~#3R#4RNl2@yPMhBFhP-aQinl(S}S?Al|x4*sjJ!|ch zbEqaCrHlOy@MZ`?A|Yr*JwweXf>e^r=^E5xJ|+H2R6l|shyVwexc7uv3Z(OC z7~ddEUzx-!+wKib2-oML2-bVwK$k!R?1Uj*pPMG0eAfawB?_LBEJ2z-1xyg{ z&Q&V#r-4Dz*c=*%$W&m4nNnU6=0a=%4O?R}W;h2E1T>ILHvwU^S05u-G99O1Qyy$v z%ON00!740oaWFw|;=OMNV-Y-poMT}$>m_(S?nmmaAL($$-%xDJvNigiXBvCzYv3Fu zC@8fZ(o1V9xr5=7VXEcd;`F2&rO#hhkPw7N#`f6XBQfT`#lz`@ z4ghWbV}KxL2>2kfz+}#u|FyFUpkZs*Z8p%eQ$?q;Va+#)P zj&@}8uUS)lM8rct+3VMGytipWK9$ThX;lX7K}D%XEk))&R2nXbwd&$8k%XD|hqOl) z9?hucC)C?lq_q3oQjT(Ve7Sd;+n>rSFtIOBQJ?m53~JjBk! z<#{t%Bn;Z(k~bdq4C$-_V+tbfp zzIBfr2;`1Ma|bZ=Sb@OehIf<86B=JjrNnpav}{aaM>k~c$jV(&eM!7c*6)+?s)BT(&4l3 z%zJGD+Qtq`gMBN6Geu*9#n-RnK2(GK9BRp&mvJZSpRtJsUfjo?Yz;w<6)SyO$LjI7}E+|HFn zcuUA+BDLHdi<1~}H*b7iR%+{`;a~D*mF6W{DYTR3`@RTh>U38r8v-YOb?9}@>8ZLB zbvUNK&987sC2qI;xh{6<(;TgO=)sDl{e&dh$bp&sLnqX1mT&3l-Q$@q41vuVW!D9T zyj$Do6H(FAINUHI99ClIa~=lf*M9KG?NZ#yjlY3kJN$K$sbYe-n^D%GO~GB`Jds+4 zDiYR{Zek6->8_h14;Kcuo{&?$+RBm-NI6f~#CP@Yt!mQTqBONr=IWOCzU?+MFL&*( zs(Dz|JZ#rL71)2Nr8V{OBI^B5|Cx2Z^QQE4s`cF_!DQOtBhD``&IVsz*r0st`Qi4o z=Sq}vD(k^jX?A!1(J%ioCVBERSrrv@*tR5d>D8;tpFIlt#^+yWVvS{vBl~_I<@BLq ze-vj1ml6WZ_?{>NLitSkJGdhv68FT)*sFEtM@c%Li7o(Td`l?23-9${g1-dbjr^H7 zXG5SgTtfaR4u-)v6v@F z5HM}BEfC~jEPIzEGI76*y-OM^O_Vw6me9wlP=>3ff$mu&Ed*V3zz{BluC78R>H)S1 zbrs}7CN9ASZuF@9VVln2`7K?09J0KIWpdbqh+&%*}BAsUs z1WYHm&~?BnfQcJ)2A%;l2*Mx;;{bVpj>(1qxPTc~^LeH{Fqq>GJ_Ia=3c8qyyX*-z zigi6iLqPQcrQVEHy1|wjz~}j&YuFXW5d^*#a^a}T2PT-?&Fy5upF8>g?PD+`{)ECH KA_&IQx%>ecMfd^$ delta 2774 zcma);dpwl+9>?c~$2AG#HuNyq#+~D~MH$6h7!zCA?6%`t%ot`Un=);YZBKG}Qqc;r z#D+3XvQbEzERF5;3cDhbOD-u%DRO=@TP^3j%sJ0L&+YyF{662$?|J=x&rm-4$OW`6 zla9ryqaX-Exv+EnP|J>WNIymMicK5ZL=FnN-#glhMxkotQK;1_khPdafHDsCsPKDvC0W1{nkJ2&%b-yG+N;Y7%h$|^ z+48D_+AdiXN|mrWi*({OkycHy4L$YJixa>`w3G&gop?wb#{b1W)AEx6Y@#2r!4X(4 zFkjt<&|hB<2iWj8BWx57g}PuYg*|R)E%xxsy2f9$K)Tb?5*8Va0`%;y-llrcT7?u; z*iICQWp9^XRHy>n5=d^fi4$HiIf()61q*aV6!>;t0n4khJ2u>d0K1X<_(e5@LQ$lA z?m}nVDzS&2h1mND@bFgJRJ^wf!U9(RnFY=ha8`;^Jj6H}i*;*R_KEU9w@+FQKReBc zr|l*4^SW*@ZOJ-PbcL>SVqLc{EGr%XO>Jp8+}yHL%zD%oTrvZ!qBPcwEXjASQ{6Ti ztDBuB6vNikHwHMV6^`gKq2DhBohg;6RL%cgAluC|Exkl0=T1JE zImSBmw4glhkm%fX4MTft$ZVgYYzIH{H8heSP?C$c-Bojm5tq$(j0r-@27q@p3{(vER)YEuEOz$Upi0kc-8Van5H?axy#(Z#f?@zilwfOTSyD%Gobf5We<8ha)-BFuV>zw z4vcS{Ae30|YPqJImNxF7ZDHevx$9hnPkqYlUmw43kc2yV`dFJInYg}MJZkk`u>6VwWDj{&wxn$hcj7BvzrIa^? z#>C4h<`!@hlcMFVDF28^tVszt{;at$qjkK;6e9PO$ zxl9Y(m&23`g;@~8EGIp?K0)~E;Y<9r3cKv~)Q9L3CUtm0)!^4X56^FF(DHQ`*iah| zD!;1UMrgZQb$K`=QQOD&jw7?y@z9dU#Cj*2TNm%~ zMrDGXup_Wht-$~FpDAPEglGLz3kE)q@zY0NTE6JZTY7IgM!l6NNL!eAojE)=-U;9G zhadSE)&_npr*}ga<%#AP^bP73HBS~BgdE&cdP-}sf%t{ zD9M>unhomo5Is@eUrbIS=Iv-5H3@IaI zS&`5fbrkp+K&>IYEQKi)zDvQQI`Sko0SBs{W-E+#%c2+%?V zk_Y*87(NE};{>)<$Q~9ij&H?}`<5MMv(nxmpqBLii?Urw8F|43H>1BOE{78q9moF< z9hMi2IYUH}y)1|y|FD*CSy~^%m7I%>kxrrxGVLYqq`(A^tnpSvnRJ_L4G5pZpaaWHI2a zXPqQ(<5Dqm~|_B4>==MP8!SR@hAhfX%f&Fa~i)tjId%pBniv=JJnoWmcXZpg=>sl^`8HN(zHu|5v1mUHu!X C@kS&7 diff --git a/xmlDrawing.go b/xmlDrawing.go index 3e54b7207f..04956d5745 100644 --- a/xmlDrawing.go +++ b/xmlDrawing.go @@ -107,6 +107,7 @@ const ( MaxFieldLength = 255 MaxColumnWidth = 255 MaxRowHeight = 409 + MaxCellStyles = 64000 MinFontSize = 1 TotalRows = 1048576 MinColumns = 1 From 234c5541567e721c33cf433846619ed7aece12d8 Mon Sep 17 00:00:00 2001 From: Thomas Charbonnel Date: Mon, 25 Jul 2022 15:18:03 +0800 Subject: [PATCH 3/4] #1284 WIP --- cell_test.go | 78 ++++++++++++++++++++++++------------------------ excelize_test.go | 4 +-- rows.go | 73 +++++++++++++++++++++++++++++++++++++++----- rows_test.go | 7 ++--- 4 files changed, 108 insertions(+), 54 deletions(-) diff --git a/cell_test.go b/cell_test.go index fb1e8ef585..48a1c2fb77 100644 --- a/cell_test.go +++ b/cell_test.go @@ -224,7 +224,7 @@ func TestGetCellValue(t *testing.T) { f.checked = nil cells := []string{"A3", "A4", "B4", "A7", "B7"} rows, err := f.GetRows("Sheet1") - assert.Equal(t, [][]string{nil, nil, {"A3"}, {"A4", "B4"}, nil, nil, {"A7", "B7"}, {"A8", "B8"}}, rows) + assert.Equal(t, [][]Cell{nil, nil, {Cell{Value: "A3"}}, {Cell{Value: "A4"}, Cell{Value: "B4"}}, nil, nil, {Cell{Value: "A7"}, Cell{Value: "B7"}}, {Cell{Value: "A8"}, Cell{Value: "B8"}}}, rows) assert.NoError(t, err) for _, cell := range cells { value, err := f.GetCellValue("Sheet1", cell) @@ -246,21 +246,21 @@ func TestGetCellValue(t *testing.T) { f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `A2B2`))) f.checked = nil rows, err = f.GetRows("Sheet1") - assert.Equal(t, [][]string{nil, {"A2", "B2"}}, rows) + assert.Equal(t, [][]Cell{nil, {Cell{Value: "A2"}, Cell{Value: "B2"}}}, rows) assert.NoError(t, err) f.Sheet.Delete("xl/worksheets/sheet1.xml") f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `A1B1`))) f.checked = nil rows, err = f.GetRows("Sheet1") - assert.Equal(t, [][]string{{"A1", "B1"}}, rows) + assert.Equal(t, [][]Cell{{Cell{Value: "A1"}, Cell{Value: "B1"}}}, rows) assert.NoError(t, err) f.Sheet.Delete("xl/worksheets/sheet1.xml") f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `A3A4B4A7B7A8B8`))) f.checked = nil rows, err = f.GetRows("Sheet1") - assert.Equal(t, [][]string{{"A3"}, {"A4", "B4"}, nil, nil, nil, nil, {"A7", "B7"}, {"A8", "B8"}}, rows) + assert.Equal(t, [][]Cell{{Cell{Value: "A3"}}, {Cell{Value: "A4"}, Cell{Value: "B4"}}, nil, nil, nil, nil, {Cell{Value: "A7"}, Cell{Value: "B7"}}, {Cell{Value: "A8"}, Cell{Value: "B8"}}}, rows) assert.NoError(t, err) f.Sheet.Delete("xl/worksheets/sheet1.xml") @@ -270,13 +270,13 @@ func TestGetCellValue(t *testing.T) { assert.Equal(t, "H6", cell) assert.NoError(t, err) rows, err = f.GetRows("Sheet1") - assert.Equal(t, [][]string{ - {"A6", "B6", "C6"}, + assert.Equal(t, [][]Cell{ + {Cell{Value: "A6"}, Cell{Value: "B6"}, Cell{Value: "C6"}}, nil, - {"100", "B3"}, - {"", "", "", "", "", "F4"}, + {Cell{Value: int64(100)}, Cell{Value: "B3"}}, + {Cell{}, Cell{}, Cell{}, Cell{}, Cell{}, Cell{Value: "F4"}}, nil, - {"", "", "", "", "", "", "", "H6"}, + {Cell{}, Cell{}, Cell{}, Cell{}, Cell{}, Cell{}, Cell{}, Cell{Value: "H6"}}, }, rows) assert.NoError(t, err) @@ -314,36 +314,36 @@ func TestGetCellValue(t *testing.T) { `))) f.checked = nil rows, err = f.GetRows("Sheet1") - assert.Equal(t, [][]string{{ - "2422.3", - "2422.3", - "12.4", - "964", - "1101.6", - "275.4", - "68.9", - "44385.2083333333", - "5.1", - "5.11", - "5.1", - "5.111", - "5.1111", - "2422.012345678", - "2422.0123456789", - "12.012345678901", - "964", - "1101.6", - "275.4", - "68.9", - "0.08888", - "0.00004", - "2422.3", - "1101.6", - "275.4", - "68.9", - "1.1", - "1234567890123_4", - "123456789_0123_4", + assert.Equal(t, [][]Cell{{ + Cell{Value: 2422.3}, + Cell{Value: 2422.3}, + Cell{Value: 12.4}, + Cell{Value: int64(964)}, + Cell{Value: 1101.6}, + Cell{Value: 275.4}, + Cell{Value: 68.9}, + Cell{Value: 44385.2083333333}, + Cell{Value: 5.1}, + Cell{Value: 5.11}, + Cell{Value: 5.1}, + Cell{Value: 5.111}, + Cell{Value: 5.1111}, + Cell{Value: 2422.012345678}, + Cell{Value: 2422.0123456789}, + Cell{Value: 12.012345678901}, + Cell{Value: int64(964)}, + Cell{Value: 1101.6}, + Cell{Value: 275.4}, + Cell{Value: 68.9}, + Cell{Value: 0.08888}, + Cell{Value: 0.00004}, + Cell{Value: 2422.3}, + Cell{Value: 1101.6}, + Cell{Value: 275.4}, + Cell{Value: 68.9}, + Cell{Value: 1.1}, + Cell{Value: "1234567890123_4"}, + Cell{Value: "123456789_0123_4"}, }}, rows) assert.NoError(t, err) } diff --git a/excelize_test.go b/excelize_test.go index f1b9903cbb..20368d0275 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -1114,12 +1114,12 @@ func TestSharedStrings(t *testing.T) { if !assert.NoError(t, err) { t.FailNow() } - assert.Equal(t, "A", rows[0][0]) + assert.Equal(t, Cell{Value: "A"}, rows[0][0]) rows, err = f.GetRows("Sheet2") if !assert.NoError(t, err) { t.FailNow() } - assert.Equal(t, "Test Weight (Kgs)", rows[0][0]) + assert.Equal(t, Cell{Value: "Test Weight (Kgs)"}, rows[0][0]) assert.NoError(t, f.Close()) } diff --git a/rows.go b/rows.go index 9ef52cadd4..4f29f34cee 100644 --- a/rows.go +++ b/rows.go @@ -49,12 +49,12 @@ import ( // fmt.Println() // } // -func (f *File) GetRows(sheet string, opts ...Options) ([][]string, error) { +func (f *File) GetRows(sheet string, opts ...Options) ([][]Cell, error) { rows, err := f.Rows(sheet) if err != nil { return nil, err } - results, cur, max := make([][]string, 0, 64), 0, 0 + results, cur, max := make([][]Cell, 0, 64), 0, 0 for rows.Next() { cur++ row, err := rows.Columns(opts...) @@ -147,7 +147,7 @@ func (rows *Rows) Close() error { // Columns return the current row's column values. This fetches the worksheet // data as a stream, returns each cell in a row as is, and will not skip empty // rows in the tail of the worksheet. -func (rows *Rows) Columns(opts ...Options) ([]string, error) { +func (rows *Rows) Columns(opts ...Options) ([]Cell, error) { if rows.curRow > rows.seekRow { return nil, nil } @@ -190,9 +190,9 @@ func (rows *Rows) Columns(opts ...Options) ([]string, error) { } // appendSpace append blank characters to slice by given length and source slice. -func appendSpace(l int, s []string) []string { +func appendSpace(l int, s []Cell) []Cell { for i := 1; i < l; i++ { - s = append(s, "") + s = append(s, Cell{}) } return s } @@ -211,7 +211,7 @@ type rowXMLIterator struct { err error inElement string cellCol int - columns []string + columns []Cell } // rowXMLHandler parse the row XML element of the worksheet. @@ -225,9 +225,13 @@ func (rows *Rows) rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.Sta return } } + //blank := rowIterator.cellCol - len(rowIterator.columns) + //if val, _ := colCell.getValueFrom(rows.f, rows.sst, raw); val != "" || colCell.F != nil { + // rowIterator.columns = append(appendSpace(blank, rowIterator.columns), val) + //} blank := rowIterator.cellCol - len(rowIterator.columns) - if val, _ := colCell.getValueFrom(rows.f, rows.sst, raw); val != "" || colCell.F != nil { - rowIterator.columns = append(appendSpace(blank, rowIterator.columns), val) + if val, _ := colCell.getTypedValueFrom(rows.f, rows.sst); val != "" || colCell.F != nil { + rowIterator.columns = append(appendSpace(blank, rowIterator.columns), Cell{Value: val, StyleID: colCell.S}) } } } @@ -494,6 +498,59 @@ func (c *xlsxC) getValueFrom(f *File, d *xlsxSST, raw bool) (string, error) { } } +func (c *xlsxC) getTypedValueFrom(f *File, d *xlsxSST) (interface{}, error) { + f.Lock() + defer f.Unlock() + switch c.T { + case "b": + if c.V == "1" { + return true, nil + } else if c.V == "0" { + return false, nil + } + case "s": + if c.V != "" { + xlsxSI := 0 + xlsxSI, _ = strconv.Atoi(c.V) + if _, ok := f.tempFiles.Load(defaultXMLPathSharedStrings); ok { + return f.getFromStringItem(xlsxSI), nil + } + if len(d.SI) > xlsxSI { + return d.SI[xlsxSI].String(), nil + } + } + case "str": + return c.V, nil + case "inlineStr": + if c.IS != nil { + return c.IS.String(), nil + } + return c.V, nil + default: + if isNum, precision := isNumeric(c.V); isNum { + var precisionV string + if precision == 0 { + precisionV = roundPrecision(c.V, 15) + } else { + precisionV = roundPrecision(c.V, -1) + } + + vi, erri := strconv.ParseInt(precisionV, 10, 64) + vf, errf := strconv.ParseFloat(precisionV, 64) + if erri == nil { + return vi, nil + } else if errf == nil { + return vf, nil + } else { + return precisionV, nil + } + } + // TODO: add support for other possible values of T (https://stackoverflow.com/questions/18334314/what-do-excel-xml-cell-attribute-values-mean) + } + + return c.V, 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. diff --git a/rows_test.go b/rows_test.go index ec25bd938a..019fd360d2 100644 --- a/rows_test.go +++ b/rows_test.go @@ -24,11 +24,11 @@ func TestRows(t *testing.T) { t.FailNow() } - var collectedRows [][]string + var collectedRows [][]Cell for rows.Next() { columns, err := rows.Columns() assert.NoError(t, err) - collectedRows = append(collectedRows, trimSliceSpace(columns)) + collectedRows = append(collectedRows, columns) } if !assert.NoError(t, rows.Error()) { t.FailNow() @@ -37,9 +37,6 @@ func TestRows(t *testing.T) { returnedRows, err := f.GetRows(sheet2) assert.NoError(t, err) - for i := range returnedRows { - returnedRows[i] = trimSliceSpace(returnedRows[i]) - } if !assert.Equal(t, collectedRows, returnedRows) { t.FailNow() } From 4db31efb5618c7bb6779ba95de8b42538d8f4a28 Mon Sep 17 00:00:00 2001 From: Thomas Charbonnel Date: Wed, 27 Jul 2022 14:42:50 +0800 Subject: [PATCH 4/4] #1284 WIP add formula --- lib.go | 3 +++ rows.go | 15 +++++++++++---- rows_test.go | 18 ++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/lib.go b/lib.go index 99118ff079..75d24a30c4 100644 --- a/lib.go +++ b/lib.go @@ -698,6 +698,9 @@ func (f *File) addSheetNameSpace(sheet string, ns xml.Attr) { // the precision for the numeric. func isNumeric(s string) (bool, int) { dot, e, n, p := false, false, false, 0 + if s == "" { + return false, 0 + } for i, v := range s { if v == '.' { if dot { diff --git a/rows.go b/rows.go index 4f29f34cee..e661670615 100644 --- a/rows.go +++ b/rows.go @@ -74,7 +74,8 @@ type Rows struct { err error curRow, seekRow int needClose, rawCellValue bool - sheet string + sheetPath string + sheetName string f *File tempFile *os.File sst *xlsxSST @@ -230,9 +231,13 @@ func (rows *Rows) rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.Sta // rowIterator.columns = append(appendSpace(blank, rowIterator.columns), val) //} blank := rowIterator.cellCol - len(rowIterator.columns) - if val, _ := colCell.getTypedValueFrom(rows.f, rows.sst); val != "" || colCell.F != nil { - rowIterator.columns = append(appendSpace(blank, rowIterator.columns), Cell{Value: val, StyleID: colCell.S}) + var formula string + if colCell.F != nil { + formula, _ = rows.f.GetCellFormula(rows.sheetName, colCell.R) } + //if val, _ := colCell.getTypedValueFrom(rows.f, rows.sst); val != "" || colCell.F != nil { + val, _ := colCell.getTypedValueFrom(rows.f, rows.sst) + rowIterator.columns = append(appendSpace(blank, rowIterator.columns), Cell{Value: val, StyleID: colCell.S, Formula: formula}) } } @@ -272,7 +277,7 @@ func (f *File) Rows(sheet string) (*Rows, error) { f.saveFileList(name, f.replaceNameSpaceBytes(name, output)) } var err error - rows := Rows{f: f, sheet: name} + rows := Rows{f: f, sheetPath: name, sheetName: sheet} rows.needClose, rows.decoder, rows.tempFile, err = f.xmlDecoder(name) return &rows, err } @@ -544,6 +549,8 @@ func (c *xlsxC) getTypedValueFrom(f *File, d *xlsxSST) (interface{}, error) { } else { return precisionV, nil } + } else { + return nil, nil } // TODO: add support for other possible values of T (https://stackoverflow.com/questions/18334314/what-do-excel-xml-cell-attribute-values-mean) } diff --git a/rows_test.go b/rows_test.go index 019fd360d2..fbff60441e 100644 --- a/rows_test.go +++ b/rows_test.go @@ -69,12 +69,30 @@ func TestRowsIterator(t *testing.T) { rows, err := f.Rows(sheetName) require.NoError(t, err) + expectedCells := [][]Cell{ + {Cell{Value: "Monitor", StyleID: 1}, Cell{StyleID: 1}, Cell{Value: "Brand", StyleID: 2}, Cell{StyleID: 2}, Cell{Value: "inlineStr"}}, + {Cell{Value: "> 23 Inch", StyleID: 1}, Cell{Value: int64(19), StyleID: 1}, Cell{Value: "HP", StyleID: 3}, Cell{Value: int64(200), StyleID: 4}}, + {Cell{Value: "20-23 Inch", StyleID: 1}, Cell{Value: int64(24), StyleID: 1}, Cell{Value: "DELL", StyleID: 3}, Cell{Value: int64(450), StyleID: 4}}, + {Cell{Value: "17-20 Inch", StyleID: 1}, Cell{Value: int64(56), StyleID: 1}, Cell{Value: "Lenove", StyleID: 3}, Cell{Value: int64(200), StyleID: 4}}, + {Cell{Value: "< 17 Inch", StyleID: 5}, Cell{Value: int64(21), StyleID: 1}, Cell{Value: "SONY", StyleID: 3}, Cell{Value: int64(510), StyleID: 4}}, + {Cell{}, Cell{}, Cell{Value: "Acer", StyleID: 3}, Cell{Value: int64(315), StyleID: 4}}, + {Cell{}, Cell{}, Cell{Value: "IBM", StyleID: 3}, Cell{Value: int64(127), StyleID: 4}}, + {Cell{}, Cell{}, Cell{Value: "ASUS", StyleID: 4}, Cell{Value: int64(89), StyleID: 4}}, + {Cell{}, Cell{}, Cell{Value: "Apple", StyleID: 4}, Cell{Value: int64(348), StyleID: 4}}, + {Cell{}, Cell{}, Cell{Value: "SAMSUNG", StyleID: 4}, Cell{Value: int64(53), StyleID: 4}}, + {Cell{}, Cell{}, Cell{Value: "Other", StyleID: 4}, Cell{Value: int64(37), StyleID: 4}, Cell{Formula: "B2+B3", StyleID: 4}, Cell{Formula: "IF(B2>0, (D2/B2)*100, 0)", StyleID: 4}, Cell{Formula: "IF(B2>0, (D2/B2)*100, 0)", StyleID: 4}, Cell{Formula: "IF(D2>0, (F2/D2)*100, 0)", StyleID: 4}, Cell{Formula: "IF(D2>0, (F2/D2)*100, 0)", StyleID: 4}}, + } + gotCells := [][]Cell{} for rows.Next() { rowCount++ require.True(t, rowCount <= expectedNumRow, "rowCount is greater than expected") + cols, err := rows.Columns() + require.NoError(t, err) + gotCells = append(gotCells, cols) } assert.Equal(t, expectedNumRow, rowCount) + assert.Equal(t, expectedCells, gotCells) assert.NoError(t, rows.Close()) assert.NoError(t, f.Close())