@@ -18,6 +18,7 @@ import (
18
18
"io"
19
19
"math"
20
20
"os"
21
+ "regexp"
21
22
"strconv"
22
23
23
24
"github.com/mohae/deepcopy"
@@ -211,6 +212,15 @@ func (err ErrSheetNotExist) Error() string {
211
212
return fmt .Sprintf ("sheet %s does not exist" , err .SheetName )
212
213
}
213
214
215
+ // ErrCountRows defines an error of count rows
216
+ type ErrCountRows struct {
217
+ err error
218
+ }
219
+
220
+ func (err ErrCountRows ) Error () string {
221
+ return fmt .Sprintf ("wrong count rows: %s" , err .err .Error ())
222
+ }
223
+
214
224
// rowXMLIterator defined runtime use field for the worksheet row SAX parser.
215
225
type rowXMLIterator struct {
216
226
err error
@@ -237,6 +247,56 @@ func (rows *Rows) rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.Sta
237
247
}
238
248
}
239
249
250
+ // CountRows returns the number of rows in the worksheet.
251
+ // if return -1, that row not found
252
+ func (f * File ) CountRows (sheet string ) (int64 , error ) {
253
+ name , ok := f .getSheetXMLPath (sheet )
254
+ if ! ok {
255
+ return - 1 , ErrSheetNotExist {sheet }
256
+ }
257
+
258
+ needClose , reader , tempFile , size , err := f .contentReader (name )
259
+ if err != nil {
260
+ return - 1 , ErrCountRows {fmt .Errorf ("content reader: %v" , err )}
261
+ }
262
+ if needClose && err == nil {
263
+ defer tempFile .Close ()
264
+ }
265
+
266
+ var contentSize int64 = 1024
267
+ var content = make ([]byte , contentSize )
268
+ var start int64
269
+ if size - contentSize < 0 {
270
+ start = 0
271
+ } else {
272
+ start = size - contentSize
273
+ }
274
+
275
+ if _ , err = reader .ReadAt (content , start ); err != nil && err != io .EOF {
276
+ return - 1 , ErrCountRows {fmt .Errorf ("read at: %v" , err )}
277
+ }
278
+
279
+ indexStart := bytes .LastIndex (content , []byte (`<row` ))
280
+ if indexStart == - 1 {
281
+ return 0 , ErrCountRows {fmt .Errorf ("not found row tag" )}
282
+ }
283
+
284
+ indexStop := bytes .Index (content [indexStart :], []byte (`>` ))
285
+ if indexStop == - 1 {
286
+ return - 1 , ErrCountRows {fmt .Errorf ("not found end row" )}
287
+ }
288
+ indexStop = indexStart + indexStop
289
+
290
+ rFind := regexp .MustCompile (`r="(\d+)"` ).Find (content [indexStart :indexStop ])
291
+ if len (rFind ) == 0 {
292
+ return - 1 , ErrCountRows {fmt .Errorf ("not found row number" )}
293
+ }
294
+
295
+ countStr := string (rFind [3 : len (rFind )- 1 ])
296
+
297
+ return strconv .ParseInt (countStr , 10 , 64 )
298
+ }
299
+
240
300
// Rows returns a rows iterator, used for streaming reading data for a
241
301
// worksheet with a large data. This function is concurrency safe. For
242
302
// example:
@@ -326,19 +386,38 @@ func (f *File) getFromStringItem(index int) string {
326
386
return f .getFromStringItem (index )
327
387
}
328
388
329
- // xmlDecoder creates XML decoder by given path in the zip from memory data
389
+ type ReaderContent interface {
390
+ io.Reader
391
+ io.ReaderAt
392
+ }
393
+
394
+ // contentReader returns reader by given path in the zip from memory data
330
395
// or system temporary file.
331
- func (f * File ) xmlDecoder (name string ) (bool , * xml. Decoder , * os.File , error ) {
396
+ func (f * File ) contentReader (name string ) (bool , ReaderContent , * os.File , int64 , error ) {
332
397
var (
333
398
content []byte
334
399
err error
335
400
tempFile * os.File
336
401
)
337
402
if content = f .readXML (name ); len (content ) > 0 {
338
- return false , f . xmlNewDecoder ( bytes .NewReader (content )) , tempFile , err
403
+ return false , bytes .NewReader (content ), tempFile , int64 ( len ( content )) , err
339
404
}
405
+
340
406
tempFile , err = f .readTemp (name )
341
- return true , f .xmlNewDecoder (tempFile ), tempFile , err
407
+
408
+ fileStat , err := tempFile .Stat ()
409
+ if err != nil {
410
+ return true , tempFile , tempFile , 0 , fmt .Errorf ("failed to get file stat: %w" , err )
411
+ }
412
+
413
+ return true , tempFile , tempFile , fileStat .Size (), err
414
+ }
415
+
416
+ // xmlDecoder creates XML decoder by given path in the zip from memory data
417
+ // or system temporary file.
418
+ func (f * File ) xmlDecoder (name string ) (bool , * xml.Decoder , * os.File , error ) {
419
+ needClose , reader , tempFile , _ , err := f .contentReader (name )
420
+ return needClose , f .xmlNewDecoder (reader ), tempFile , err
342
421
}
343
422
344
423
// SetRowHeight provides a function to set the height of a single row. For
0 commit comments