当前位置: 首页 > news >正文

GO excelize 读取excel进行时间类型转换(自动转换)

GO excelize 读取excel进行时间类型转换(自动转换)
需求分析

需求:如何自动识别excel中的时间类型数据并转化成对应的 "Y-m-d H:i:s"类型数据。

分析:excelize在读取excel时,GetRows() 返回的都是字符串类型,并且有些时间类型的数据会进行转换,如果全部转化成 float64 格式,然后转换成对应的字符串,并且excelize提供函数

func ExcelDateToTime(excelDate float64, use1904Format bool) (time.Time, error) {...
}

可以将float64 转换成time.Time 类型,time.Time 很容易转化成对应"Y-m-d H:i:s"格式字符串类型数据。所以我们的难点就在于如何自动识别excel中时日期时间类型数据

excel 单元格格式

以下3月1日数据写入到excel中,excel都会识别成2024/3/1,但是对应单元格格式不同,转化为常规类型的话大部分都相同

  • 2024年3月1日------------- yyyy"年"m"月"d"日"-------------453352
  • 2024/3/1------------- yyyy/m/d-------------453352
  • Mar-24------------- mmm-yy-------------453352
  • 2024年3月------------- yyyy"年"m"月"-------------453352
  • 2024/3/1 0:00-------------yyyy/m/d h:mm-------------453352
  • '2024-03-01 00:00:00-------------通用-------------2024-03-01 00:00:00
  • excelize 读取
    func parseFileUrl(filePath string) ([]map[string]string, error) {f, err := excelize.OpenFile(filePath)if err != nil {return nil, err}sheetName := f.GetSheetName(0)rows, err := f.GetRows(sheetName)if err != nil {return nil, err}if len(rows) > 0 {for rowKey, cols := range rows {if len(cols) > 0 {for colKey, value := range cols {fmt.Println(rowKey, "-", colKey, ":", value)}}}}return nil, err
    }
    

    结果打印

    0 - 0 : 45352
    1 - 0 : 03-01-24
    2 - 0 : Mar-24
    3 - 0 : 45352
    4 - 0 : 3/1/24 00:00
    5 - 0 : 2024-03-01 00:00:00
    

    由此我们可以看出时间类型打印出来的内容不尽相同,这里我们可以想办法把他们全部转化成45352

    这里我们就把他们转化成常规类型,常规类型的数据大部分都为45352

    func parseFileUrl(filePath string) ([]map[string]string, error) {f, err := excelize.OpenFile(filePath)if err != nil {return nil, err}sheetName := f.GetSheetName(0)rows, err := f.GetRows(sheetName)if err != nil {return nil, err}//转化为常规类型,对应style NumFmt 0styleId, _ := f.NewStyle(&excelize.Style{NumFmt: 0})//示例例中都放入A列,所以将A列数据全部转化成对应的常规类型_ = f.SetColStyle(sheetName, "A", styleId)rows, err = f.GetRows(sheetName)if len(rows) > 0 {for rowKey, cols := range rows {if len(cols) > 0 {for colKey, value := range cols {fmt.Println(rowKey, "-", colKey, ":", value)}}}}return nil, err
    }
    

    再次进行打印

    0 - 0 : 45352
    1 - 0 : 45352
    2 - 0 : 45352
    3 - 0 : 45352
    4 - 0 : 45352
    5 - 0 : 2024-03-01 00:00:00
    

    这时我们就可以看到大部分数据都已经转化成了45352,所以接下来很简单将数据转化成float64类型,再转化成time.time类型,最后转化成我们想要的数据类型

    func parseFileUrl(filePath string) ([]map[string]string, error) {f, err := excelize.OpenFile(filePath)if err != nil {return nil, err}sheetName := f.GetSheetName(0)rows, err := f.GetRows(sheetName)if err != nil {return nil, err}styleId, _ := f.NewStyle(&excelize.Style{NumFmt: 0})_ = f.SetColStyle(sheetName, "A", styleId)rows, err = f.GetRows(sheetName)if len(rows) > 0 {for rowKey, cols := range rows {if len(cols) > 0 {for colKey, value := range cols {timeFloat, err := strconv.ParseFloat(value, 64)if err != nil {//err 说明无法转化成float64 那么有可能本身是字符串时间进行返回timeTime, err := time.Parse("2006-01-02 15:04:05", value)if err != nil {fmt.Println(rowKey, "-", colKey, ":", value)} else {value = timeTime.Format("2006-01-02 15:04:05")fmt.Println(rowKey, "-", colKey, ":", value)}break}timeTime, _ := excelize.ExcelDateToTime(timeFloat, false)value = timeTime.Format("2006-01-02 15:04:05")fmt.Println(rowKey, "-", colKey, ":", value)}}}}return nil, err
    }
    

    打印结果

    0 - 0 : 2024-03-01 00:00:00
    1 - 0 : 2024-03-01 00:00:00
    2 - 0 : 2024-03-01 00:00:00
    3 - 0 : 2024-03-01 00:00:00
    4 - 0 : 2024-03-01 00:00:00
    5 - 0 : 2024-03-01 00:00:00
    

    此时可以解决了我们的问题,指定对应的列,转化为常规类型,然后再转化为float64(针对无法转化的数据返回),再转化为time.time类型,最后转化成我们需要的类型

    进阶
    那么如何自动进行转化?

    其实我们可以根据单元格自定义类型来进行转化,正如上面的例子,如当单元格自定义类型为:

  • yyyy"年"m"月"d"日"
  • yyyy/m/d
  • mmm-yy
  • yyyy"年"m"月"
  • yyyy/m/d h:mm
  • ...
  • 等类型的时候我们需要将他们转化成常规类型,然后根据后续操作转化成我们想要的类型。

    如何自定类型判断呢?

    其实根据上述转化成常规类型的操作我们就可以知道是哪个字段来进行确定单元格格式类型

    	styleId, _ := f.NewStyle(&excelize.Style{NumFmt: 0})
    

    Style 中的 NumFmt 来进行决定

    我们可以看下excelize 中 针对 NumFmt 的注释(在NewStyle方法上面有详细注释),这里我只粘贴部分注释代码

    //	 Index | Format String
    //	-------+----------------------------------------------------
    //	 0     | General
    //	 1     | 0
    //	 2     | 0.00
    //	 3     | #,##0
    //	 4     | #,##0.00
    //	 5     | ($#,##0_);($#,##0)
    //	 6     | ($#,##0_);[Red]($#,##0)
    //	 7     | ($#,##0.00_);($#,##0.00)
    //	 8     | ($#,##0.00_);[Red]($#,##0.00)
    //	 9     | 0%
    //	 10    | 0.00%
    //	 11    | 0.00E+00
    //	 12    | # ?/?
    //	 13    | # ??/??
    //	 14    | m/d/yy
    //	 15    | d-mmm-yy
    //	 16    | d-mmm
    //	 17    | mmm-yy
    //	 18    | h:mm AM/PM
    //	 19    | h:mm:ss AM/PM
    //	 20    | h:mm
    //	 21    | h:mm:ss
    //	 22    | m/d/yy h:mm
    //	 ...   | ...
    //	 37    | (#,##0_);(#,##0)
    //	 38    | (#,##0_);[Red](#,##0)
    //	 39    | (#,##0.00_);(#,##0.00)
    //	 40    | (#,##0.00_);[Red](#,##0.00)
    //	 41    | _(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)
    //	 42    | _($* #,##0_);_($* (#,##0);_($* "-"_);_(@_)
    //	 43    | _(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)
    //	 44    | _($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)
    //	 45    | mm:ss
    //	 46    | [h]:mm:ss
    //	 47    | mm:ss.0
    //	 48    | ##0.0E+0
    //	 49    | @// Number format code in zh-cn language:
    //
    //	 Index | Symbol
    //	-------+-------------------------------------------
    //	 27    | yyyy"年"m"月"
    //	 28    | m"月"d"日"
    //	 29    | m"月"d"日"
    //	 30    | m-d-yy
    //	 31    | yyyy"年"m"月"d"日"
    //	 32    | h"时"mm"分"
    //	 33    | h"时"mm"分"ss"秒"
    //	 34    | 上午/下午 h"时"mm"分"
    //	 35    | 上午/下午 h"时"mm"分"ss"秒
    //	 36    | yyyy"年"m"月
    //	 50    | yyyy"年"m"月
    //	 51    | m"月"d"日
    //	 52    | yyyy"年"m"月
    //	 53    | m"月"d"日
    //	 54    | m"月"d"日
    //	 55    | 上午/下午 h"时"mm"分
    //	 56    | 上午/下午 h"时"mm"分"ss"秒
    //	 57    | yyyy"年"m"月
    //	 58    | m"月"d"日"
    

    我们可以知道NumFmt 对应的值代表着各种单元格格式,此时我们可以整理一下我们需要将其转化成常规类型的数据(只做参考,并没有全部实验,自己可以把常用的实验一下)

    var ConversionTimeNumFmt = []int{14, //m/d/yy15, //d-mmm-yy17, //mmm-yy22, //m/d/yy h:mm27, // yyyy"年"m"月"30, //m-d-yy31, //yyyy"年"m"月"d"日"36, //yyyy"年"m"月50, //yyyy"年"m"月52, //yyyy"年"m"月57, //yyyy"年"m"月
    }
    

    好了,现在我们要转换的单元格格式了,那么接下来我们就可以遍历一下excel的全部单元格格式类型,看一下哪些字段需要进行转化了,不过接下来问题又来了,如何知道excel里面对应的单元格格式呢

    如何知道excel中单元格格式

    excelize 中提供方法 GetCellStyle() 可以获取该单元格的所有样式对应的styleId

    // GetCellStyle provides a function to get cell style index by given worksheet
    // name and cell reference. This function is concurrency safe.
    func (f *File) GetCellStyle(sheet, cell string) (int, error) {...
    }
    

    根据styleId 我们可以找到对应的所有样式配置 GetStyle()

    // GetStyle provides a function to get style definition by given style index.
    func (f *File) GetStyle(idx int) (*Style, error) {...
    }
    

    单元格格式 就对应的是 style.NumFmt

    这时我们只需要知道每一个单元格的styleId 对应的 style.NumFmt 就可以将数据进行转化(这里做了三个循环,第一遍是获取了对应styleId, 第二遍是更改表样式,将指定类型转化为常规类型,第三遍就是获取对应的数据)

    // parseFileUrl 解析文件流excel
    func parseFileUrl(filePath string) ([]map[string]string, error) {f, err := excelize.OpenFile(filePath)if err != nil {return nil, err}sheetName := f.GetSheetName(0)rows, err := f.GetRows(sheetName)if err != nil {return nil, err}//读取excel 所有styleId 数组styles := make([]int, 0)//所有需要更改单元格格式的 styleId 数组needChangeStyleIds := make([]int, 0)//所更改的cellsneedChangeCells := make([]string, 0)if len(rows) > 0 {//需要转化成的 style 对应style IdstyleIdZero, _ := f.NewStyle(&excelize.Style{NumFmt: 0})for rowKey, cols := range rows {if len(cols) > 0 {for colKey, _ := range cols {columnNumber, _ := excelize.CoordinatesToCellName(colKey+1, rowKey+1)styleId, _ := f.GetCellStyle(sheetName, columnNumber)if !arrayHelper.InArray(styles, styleId) {styles = append(styles, styleId)}}}}fmt.Println(styles)if len(styles) > 0 {for _, styleId := range styles {style, _ := f.GetStyle(styleId)if arrayHelper.InArray(ConversionTimeNumFmt, style.NumFmt) {needChangeStyleIds = append(needChangeStyleIds, styleId)}}}for rowKey, cols := range rows {if len(cols) > 0 {for colKey, _ := range cols {columnNumber, _ := excelize.CoordinatesToCellName(colKey+1, rowKey+1)styleId, _ := f.GetCellStyle(sheetName, columnNumber)if arrayHelper.InArray(needChangeStyleIds, styleId) {_ = f.SetCellStyle(sheetName, columnNumber, columnNumber, styleIdZero)needChangeCells = append(needChangeCells, columnNumber)}}}}rows, err = f.GetRows(sheetName)if err != nil {return nil, err}if len(rows) > 0 {for rowKey, cols := range rows {if len(cols) > 0 {for colKey, value := range cols {columnNumber, _ := excelize.CoordinatesToCellName(colKey+1, rowKey+1)if arrayHelper.InArray(needChangeCells, columnNumber) {timeFloat, err := strconv.ParseFloat(value, 64)if err != nil {//err 说明无法转化成float64 那么有可能本身是字符串时间进行返回timeTime, err := time.Parse("2006-01-02 15:04:05", value)if err != nil {fmt.Println(rowKey, "-", colKey, ":", value)} else {value = timeTime.Format("2006-01-02 15:04:05")fmt.Println(rowKey, "-", colKey, ":", value)}break}timeTime, _ := excelize.ExcelDateToTime(timeFloat, false)value = timeTime.Format("2006-01-02 15:04:05")fmt.Println(rowKey, "-", colKey, ":", value)}}}}}}return nil, err
    }var ConversionTimeNumFmt = []int{14, //m/d/yy15, //d-mmm-yy17, //mmm-yy22, //m/d/yy h:mm27, // yyyy"年"m"月"30, //m-d-yy31, //yyyy"年"m"月"d"日"36, //yyyy"年"m"月50, //yyyy"年"m"月52, //yyyy"年"m"月57, //yyyy"年"m"月
    }
    

    其中InArray方法为

    // InArray 判断元素是否再数组内
    func InArray[T int | float64 | string](array []T, value T) bool {for _, v := range array {if v == value {return true}}return false
    }
    

    通过三次遍历操作,自动转化了时间类型

相关文章:

GO excelize 读取excel进行时间类型转换(自动转换)

GO excelize 读取excel进行时间类型转换(自动转换) 需求分析 需求:如何自动识别excel中的时间类型数据并转化成对应的 "Y-m-d H:i:s"类型数据。 分析:excelize在读取excel时,GetRows() 返回的都是字符串类…...

【算法与数据结构】二分查找思想

#1024程序员节|征文# 正文: 二分查找(binary search)是一种基于分治策略的高效搜索算法。它利用数据的有序性,每轮缩小一半搜索范围,直至找到目标元素或搜索区间为空为止,其实有时候数据没有序…...

PHP PDO:安全、灵活的数据持久层解决方案

PHP PDO:安全、灵活的数据持久层解决方案 PHP PDO(PHP Data Objects)是一个轻量级的、具有兼容接口的数据持久层抽象层。它提供了一个统一的API来访问多种数据库系统,如MySQL、PostgreSQL、SQLite、Oracle等。PDO扩展在PHP 5.1.0…...

九、Linux实战案例:项目部署全流程深度解析

Linux实战案例:项目部署全流程深度解析 在当今信息技术领域,Linux服务器凭借其卓越的稳定性、安全性以及强大的性能表现,被广泛应用于各类项目部署场景之中。本文将全面深入地介绍如何将一个项目成功部署至Linux服务器的完整流程&#xff0c…...

GIS常见前端开发框架

#1024程序员节|征文# 伴随GIS的发展,陆续出现了众多开源地图框架,这些地图框架与众多行业应用融合,极大地拓展了GIS的生命力,这里介绍几个常见的GIS前端开发框架,排名不分先后。 1.Leaflet https://leafl…...

Java | Leetcode Java题解之第506题相对名次

题目: 题解: class Solution {public String[] findRelativeRanks(int[] score) {int n score.length;String[] desc {"Gold Medal", "Silver Medal", "Bronze Medal"};int[][] arr new int[n][2];for (int i 0; i &…...

数据结构 - 堆

今天我们将学习新的数据结构-堆。 01定义 堆是一种特殊的二叉树,并且满足以下两个特性: (1)堆是一棵完全二叉树; (2)堆中任意一个节点元素值都小于等于(或大于等于)左…...

html----图片按钮,商品展示

源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>图标</title><style>.box{width:…...

YOLOv11改进策略【卷积层】| ECCV-2024 小波卷积WTConv 增大感受野,降低参数量计算量,独家创新助力涨点

一、本文介绍 本文记录的是利用小波卷积WTConv模块优化YOLOv11的目标检测网络模型。WTConv的目的是在不出现过参数化的情况下有效地增加卷积的感受野,从而解决了CNN在感受野扩展中的参数膨胀问题。本文将其加入到深度可分离卷积中,有效降低模型参数量和计算量,并二次创新C3…...

redis高级篇之redis源码分析List类型quicklist底层演变 答疑159节

(1)ziplist压缩配置:list-compress-depth 0 表示一个quicklist两端不被压缩的节点个数。这里的节点是指quicklist双向链表的节点&#xff0c;而不是指ziplist里面的数据项个数参数list-compress-depth的取值含义如下: 0:是个特殊值&#xff0c;表示都不压缩。这是Redis的默认值…...

Elasticsearch 与 Lucene 的区别和联系

Elasticsearch 与 Lucene 的区别和联系 Elasticsearch 与 Lucene 的区别和联系一、知识背景Elasticsearch 简介Lucene 简介 二、Elasticsearch 和 Lucene 的区别适用场景性能优势和劣势架构设计的异同点 三、Elasticsearch和Lucene的联系四、Elasticsearch和Lucene的应用案例及…...

OpenCV视觉分析之运动分析(5)背景减除类BackgroundSubtractorMOG2的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 基于高斯混合模型的背景/前景分割算法。 该类实现了在文献[320]和[319]中描述的高斯混合模型背景减除。 cv::BackgroundSubtractorMOG2 类是 O…...

【SAP Hana】X-DOC:数据仓库ETL如何抽取SAP中的CDS视图数据

【SAP Hana】X-DOC&#xff1a;数据仓库ETL如何抽取SAP中的CDS视图数据 1、无参CDS对应数据库视图2、有参CDS对应数据库表函数3、封装有参CDS为无参CDS&#xff0c;从而对应数据库视图 1、无参CDS对应数据库视图 select * from ZFCML_REP_V where mandt 300;2、有参CDS对应数…...

WPF的UpdateSourceTrigger属性

在WPF中&#xff0c;UpdateSourceTrigger属性用于控制数据绑定中何时将绑定目标&#xff08;通常是UI元素&#xff09;的值更新回绑定源&#xff08;通常是数据对象&#xff09;。这个属性有以下几个值&#xff1a; Default&#xff1a;这是默认值&#xff0c;对于不同的绑定目…...

2024-09-25 环境变量,进程地址空间

一、认识常见的环境变量 1. echo $HOME 输出当前用户对应的家目录 当用户登录系统时&#xff0c;流程如下&#xff1a; &#xff08;1&#xff09;用户登录系统后&#xff0c;系统启动Shell程序。 &#xff08;2&#xff09;启动bash shell&#xff0c;准备接收用户指令。 &a…...

中国移动机器人将投入养老场景;华为与APUS共筑AI医疗多场景应用

AgeTech News 一周行业大事件 华为与APUS合作&#xff0c;共筑AI医疗多场景应用 中国移动展出人形机器人&#xff0c;预计投入养老等场景 作为科技与奥富能签约&#xff0c;共拓智能适老化改造领域 天与养老与香港科技园&#xff0c;共探智慧养老新模式 中山大学合作中国…...

青少年编程能力等级测评CPA C++ 四级试卷(1)

青少年编程能力等级测评CPA C 四级试卷&#xff08;1&#xff09; 一、单项选择题&#xff08;共15题&#xff0c;每题3分&#xff0c;共45分&#xff09; CP4_1_1.在面向对象程序设计中&#xff0c;与数据构成一个相互依存的整体的是&#xff08; &#xff09;。 A. 对数据…...

树上任意两点的距离

题目描述 给出 n 个点的一棵树&#xff0c;多次询问两点之间的最短距离。 注意&#xff1a;边是双向的。 输入描述 第一行为两个整数 n 和 m。n 表示点数&#xff0c;m 表示询问次数&#xff1b; 下来 n−1 行&#xff0c;每行三个整数 x,y,k&#xff0c;表示点 x 和点 y 之间…...

【 thinkphp8 】00008 thinkphp8数据查询,常用table,name方法,进行数据查询汇总

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 【 t…...

Git的命令合集

关于Git的一些命令合集&#xff0c;会慢慢更新&#xff01; 20241024程序员节开始写的&#xff0c;记录一下~~ git查看log、查看详细提交记录 会显示之前的提交记录 , 排序由近及远 git log log按q退出 git回退到某个commit命令&#xff1a; 退到/进到指定commit的sha码&…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

NPOI Excel用OLE对象的形式插入文件附件以及插入图片

static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...