go操作excel文档库excelize使用方法详解

CKeenGolanggo语言基础约 1546 字大约 5 分钟

作者:程序员CKeen
博客:http://ckeen.cnopen in new window

长期坚持做有价值的事!积累沉淀,持续成长,升维思考!希望把编码作为长期兴趣爱好😄


今天我们讲一下使用excelize操作excel。

首先熟悉一下excel的文件构成,excel分为以下结构:

  1. excel文件
  2. sheet页
  3. 行row
  4. 列col
  5. 项cell

对应结构如下图:

在这里插入图片描述
在这里插入图片描述

1. 准备工作

我们读取的文件格式如上图所示, 我们先定义一个StockInfo结构来存储相应字段

type StockInfo struct {
	ID              uint64    `gorm:"primaryKey;autoIncrement"`
	CompanyNo       string    `gorm:"column:company_no"`
	CompanyAbbr     string    `gorm:"column:company_abbr"`
	StockNo         string    `gorm:"column:stock_no"`
	StockName       string    `gorm:"column:stock_name"`
	ListingTime		time.Time 	`gorm:"column:listing_time"`
	GmtCreate 		time.Time 	`gorm:"column:gmt_create"`
	GmtModified 	time.Time 	`gorm:"column:gmt_modified"`
}

func (stock *StockInfo) TableName() string {
	return "stock_info"
}

注: 这里我们会使用gorm将数据存储到数据库,所以这里我们添加了gorm的标签。

安装excelize库:

	go get github.com/xuri/excelize/v2@v2.5.0

2. 使用excelize读取excel文件

操作步骤:

  1. 先获取文件excel.File对象,excelize提供了两个函数获取文件File对象

      func OpenFile(filename string, opt ...Options) (*File, error)
      func OpenReader(r io.Reader, opt ...Options) (*File, error)
    

    这里我们使用如下方式获取File实例:

       file, err := excelize.OpenFile("stock.xlsx")
       if err != nil {
          log.Fatalf("open excel file err, error:%+v\n", err)
       }
    

    亦可以使用如下方式:

       fileReader,err := os.OpenFile("stock.xlsx", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
       if err != nil {
         log.Fatalf("open excel file err, error:%+v\n", err)
       }
       
       file, err := excelize.OpenReader(fileReader)
       if err != nil {
         log.Fatalf("open excel file err, error:%+v\n", err)
       }
    
  2. 获取sheet, excelize库提供了如下操作的sheet的函数

    
     // 根据sheet的名称获取sheet的索引
     func (f *File) GetSheetIndex(name string) int
     // 根据sheet的索引获取sheet的名称
     func (f *File) GetSheetName(index int) (name string)
     // 获取所有sheet的名称列表
     func (f *File) GetSheetList() (list []string)
     // 获取所有sheet的索引对应的名称集合
     func (f *File) GetSheetMap() map[int]string
    

    这里我们我先通过GetSheetList获取所有的sheet名称列表,然后对每个sheet进行操作

  3. 读取excel中数据

    读取方式一:直接读取Row和Col的数据二维数组,然后进行读取操作,使用如下函数

     func (f *File) GetRows(sheet string, opts ...Options) ([][]string, error)
    

    下面代码实现按行列读取,将读取到的集合以StockInfo的结构存放到Slice

     func GetStockList1(file *excelize.File) ([]StockInfo, error){
     
     	var list []StockInfo
     
     	for _,sheetName := range file.GetSheetList() {
     
     		rows, err := file.GetRows(sheetName)
     
     		if err != nil {
     			log.Printf("excel get rows error, err:%+v\n", err)
     			return nil, err
     		}
     
     		for index, row := range rows {
     			// 跳过第一行标题
     			if index == 0 {
     				continue
     			}
     
     			lt, err := time.Parse("2006-01-02", strings.TrimSpace(row[4]))
     			if err != nil {
     				return nil, err
     			}
     
     			list = append(list, StockInfo{
     				CompanyNo:   row[0],
     				CompanyAbbr: row[1],
     				StockNo:     row[2],
     				StockName:   row[3],
     				ListingTime: lt,
     				GmtCreate:   time.Now(),
     			})
     		}
     	}
     
     	return list,nil
     }
    

    读取方式二:使用Rows函数,返回Rows的对象进行操作, 函数原型如下:

     func (f *File) Rows(sheet string) (*Rows, error)
    

    然后配合Rows对象的Next方法和Columns方法,获取列数据,如下:

     func GetStockList2(file *excelize.File) ([]StockInfo, error){
     	var list []StockInfo
     
     	for _,sheetName := range file.GetSheetList() {
     
     		rows, err := file.Rows(sheetName)
     		if err != nil{
     			return nil, err
     		}
     
     		for rows.Next() {
     			// 这里读到第一行标题的时候,跳过
     			if rows.CurrentRow() == 1 {
             // 这一句一定要调用,否则两列数据叠加在一起
     				rows.Columns()
     				continue
     			}
     
     			// 获取当前行的列
     			cols,_ := rows.Columns()
     
     			// 按列进行处理
     			lt, _ := time.Parse("2006-01-02", strings.TrimSpace(cols[4]))
     			list = append(list, StockInfo{
     				CompanyNo:  strings.TrimSpace(cols[0]) ,
     				CompanyAbbr: strings.TrimSpace(cols[1]),
     				StockNo:     strings.TrimSpace(cols[2]),
     				StockName:   strings.TrimSpace(cols[3]),
     				ListingTime: lt,
     				GmtCreate:   time.Now(),
     			})
     		}
     	}
     
     	return list,nil
     }
    

    读取方式三:使用GetCellValue函数,根据具体的sheet和Cell的行和列名称进行获取Cell值,GetCellValue函数如下:

     func (f *File) GetCellValue(sheet, axis string, opts ...Options) (string, error)
    

    直接使用file的GetCellValue的函数操作,使用excel的"A2","E4"这种行列定位cell的方法进行操作:

     func GetStockList3(file *excelize.File) ([]StockInfo, error){
     	var list []StockInfo
     
     	for _,sheetName := range file.GetSheetList() {
     
     		rows, err := file.Rows(sheetName)
     		if err != nil{
     			return nil, err
     		}
     
         // 从第二行开始读取
     		for i := 2; i <= rows.TotalRows(); i++ {
     
     			// 根据sheet中Cell的位置获取,例如A1表示第一行的第一列
     			companyNo, _ := file.GetCellValue(sheetName,fmt.Sprintf("A%d",i))
     			companyAbbr, _ := file.GetCellValue(sheetName,fmt.Sprintf("B%d",i))
     			stockNo, _ := file.GetCellValue(sheetName,fmt.Sprintf("C%d",i))
     			stockName, _ := file.GetCellValue(sheetName,fmt.Sprintf("D%d",i))
     
     			cell4, err := file.GetCellValue(sheetName, fmt.Sprintf("E%d",i))
     			if err != nil {
     				log.Printf("get cell error, err:%+v\n", err)
     			}
     			lt, _ := time.Parse("2006-01-02", strings.TrimSpace(cell4))
     
     			list = append(list, StockInfo{
     				CompanyNo:  companyNo ,
     				CompanyAbbr: companyAbbr,
     				StockNo:     stockNo,
     				StockName:   stockName,
     				ListingTime: lt,
     				GmtCreate:   time.Now(),
     			})
     		}
     	}
     
     	return list,nil
     }
    
  4. 数据读取成功后,我们使用gorm将数据存储到数据库

  dsn := "root:root@tcp(127.0.0.1:3306)/stock?charset=utf8mb4&parseTime=True&loc=Local"
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  if err != nil {
    log.Fatalf("connect mysql error:%+v\n",err)
  }

  file, err := excelize.OpenFile("stock.xlsx")
  if err != nil {
    log.Fatalf("open excel file err, error:%+v\n", err)
  }

  list1, err := GetStockList3(file)
  if err != nil {
    log.Fatalf("get stock list1 error:%+v\n", err)
  }

  log.Printf("read stock list:%+v\n", list1)

  defer file.Close()

  db.Omit("GmtModified").Create(&list1)

3. 使用excelize将数据写入excel文件

操作步骤:

  1. 首先我们从mysql数据库,将上面例子从的数据库读出来

    dsn := "root:root@tcp(127.0.0.1:3306)/stock?charset=utf8mb4&parseTime=True&loc=Local"
    	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    
    	if err != nil {
    		log.Fatalf("connect mysql error:%+v\n",err)
    	}
    
    	var stocks []StockInfo
    	db.Find(&stocks)
    
    	log.Printf("stocks:%+v\n", stocks)
    
  2. 创建excelize的file对象,用来操作excel文件

file := excelize.NewFile()
// 创建一个名字为stock的sheet页
file.NewSheet("stock")
  1. 将数据写入到excel

    写入方式一: 按行添加数据方法:

     func (f *File) SetSheetRow(sheet, axis string, slice interface{}) error
    

    第一个参数为sheet的名称,第二个参数表示从哪一列开始,第三个参数为连续写入的列的slice,代码如下:

     func WriteToExcel1(file *excelize.File, stocks []StockInfo) error {
     	
     	rowsCount := len(stocks)
     
     	for i := 1; i <= rowsCount ;i++ {
    ex 
     		file.SetSheetRow("stock",fmt.Sprintf("A%d",i),&[]interface{}{stocks[i-1].CompanyNo,
     			stocks[i-1].CompanyAbbr, stocks[i-1].StockNo, stocks[i-1].StockName})
     	}
     	return nil
     }
    

    写入方式二: 按Cell设置数据:

     func WriteToExcel2(file *excelize.File, stocks []StockInfo) error {
     
     	rowsCount := len(stocks)
     
     	for i := 1; i <= rowsCount ;i++ {
     
     		file.SetCellStr("stock",fmt.Sprintf("A%d",i),stocks[i-1].CompanyNo)
     		file.SetCellValue("stock",fmt.Sprintf("B%d",i),stocks[i-1].CompanyAbbr)
     		file.SetCellValue("stock",fmt.Sprintf("C%d",i),stocks[i-1].StockNo)
     		file.SetCellValue("stock",fmt.Sprintf("D%d",i),stocks[i-1].StockName)
     		file.SetCellValue("stock",fmt.Sprintf("E%d",i),stocks[i-1].ListingTime)
     
     	}
     	return nil
     }
    
  2. 将excel数据保存到文件

defer file.Close()
file.SaveAs("stock_2.xlsx")

参考资料

 包地址:https://pkg.go.dev/github.com/xuri/excelize/v2