C#实现Excel合并单元格数据导入数据集
目录
功能需求
将Excel里的worksheet表格导入到DataSet里,是项目应用里常用的一种操作。一般情况下,worksheet是一个标准的二维数组,如下图:
我们可以效仿 MS SQL SERVER 的一些基本导入选项,如首行是否包含数据,要导入哪个Sheet?还是遍历Sheets?
实际的情况,客户经常会提供一些合并单元格的Excel表格,如下图中的“所在部门名称”列:
再畅想一下,假设有跨列的情况如下:
解决导入,一种方法,是让客户进行单元格拆分或技术服务人员进行拆分后再导入。另一种就是我们要继续完善应用,处理实现合并单元格的自动化处理。
Excel与DataSet的映射关系
下图是 Excel 与 DataSet 的映射关系图:
1、Excel应用的Workbook对象与 DataSet 同为容器对象
2、Worksheets和Tables均代表各自的表集合
3、Worksheet与Table进行对应,产生和导入实际的数据
范例运行环境
操作系统: Windows Server 2019 DataCenter
操作系统上安装 Office Excel 2016
.net版本: .netFramework4.7.1 或以上
开发工具:VS2019 C#
Excel DCOM 配置
请参考我的文章《C# 读取Word表格到DataSet》有对Office DCOM详细配置介绍,这里不再赘述,Excel的对应配置名称如下图所示:
设计实现
组件库引入
方法设计
设计 object[] ExcelAsDataSet(string _filename,bool hastitle,string startaddress,string endaddress) 方法
返回值
方法返回object数组,共包括两个object对象,如果成功转化则 object[0] 存储 DataSet对象,否则为 null。如果不成功则 object[1] 存储string 错误信息对象,可根据object[1].ToString()!="" 来判断是否转化成功。
参数设计
- string _filename:Excel 数据源文件路径
- bool hastitle: 是否包含标题,如果设置为true,则表示首行数据为列名称定义
- string startaddress:可指定有效的起始单元格地址,不设置则默认为“A1”(即第一个单元格)
- string endaddress:可指定有效的截止单元格地址,不设置则默认为最后一个有值单元格(即XlCellType.xlCellTypeLastCell 枚举)
通过3、4参数的定义,可以定义出有效的导入矩形区域。
打开数据源并计算Sheets
object[] rv=new object[2];
rv[0]=null;
rv[1]="";
//创建一个名为ExcelApp的组件对象
// ExcelApplication excel = new ExcelApplication();
Excel.Application excel = new Excel.Application();
excel.DisplayAlerts=false;
excel.AskToUpdateLinks=false;
Excel.Workbook xb=excel.Workbooks.Add(_filename);
//获取活动的 worksheet和 excel sheet的个数,准备遍历sheets
Worksheet worksheet = (Worksheet) excel.ActiveSheet;
sheetCount=excel.Sheets.Count;
int startSheetIndex=1;
int endSheetIndex=sheetCount;
DataSet ds=new DataSet();
//遍历sheets
for (int currentIndex = startSheetIndex; currentIndex <= endSheetIndex; currentIndex++)
{
worksheet = (Worksheet)excel.Worksheets[currentIndex];
worksheet.Activate();
//处理每一个sheet.....
}
拆分合并的单元格
在获取有效的单元格区域后,就开始遍历单元格对象,判断单元格对象 MergeCells 属性即可,判断 Cell.MergeCells.ToString() == "True" 即表示该单元格为合并单元格对象。
示例代码如下:
//获取起始单元和截止单元格,以确定有效区域
Excel.Range _startcell=worksheet.Range["A1","A1"]; //默认为第一个单元格
if(startaddress!="")
{
try
{
_startcell=worksheet.Range[startaddress,startaddress];
}
catch(Exception ex)
{
rv[1]+=string.Format("{1}指定的起始单元格地址{0},不是合法的地址。\r\n",startaddress,worksheet.Name);
// KillProcessByStartTime("EXCEL",beforetime,aftertime);
continue;
}
}
Excel.Range _lastcell=worksheet.Cells.SpecialCells(XlCellType.xlCellTypeLastCell,Type.Missing);
//默认获取有值的最后一个有效的单元格
if(endaddress!="")
{
try
{
_lastcell=worksheet.Range[endaddress,endaddress];
}
catch(Exception ex)
{
rv[1]+=string.Format("{1}指定的结束单元格地址{0},不是合法的地址。\r\n",endaddress,worksheet.Name);
// KillProcessByStartTime("EXCEL",beforetime,aftertime);
// return rv;
continue;
}
}
//遍历有效区域单元格
foreach (Excel.Range aicell in worksheet.Range[_startcell,_lastcell])
{
if (aicell.MergeCells.ToString() == "True")
{
//处理合并单元格
object temp_merge_value = aicell.Value2; //备份单元格的值
int u_row = aicell.Row; //记录单元格的首行索引
int u_rows = aicell.MergeArea.Rows.Count; //记录单元格的合并区域包含的行数
int u_col = aicell.Column; //记录单元格的首列索引
int u_cols = aicell.MergeArea.Columns.Count; //记录单元格的合并区域包含的列数
aicell.MergeArea.UnMerge(); //取消合并,拆分单元格
Excel.Range new_aicell = worksheet.Range[worksheet.Cells[u_row, u_col], worksheet.Cells[u_row + u_rows - 1, u_col + u_cols - 1]]; //获取拆分后单元格后的有效区域
new_aicell.Value2 = temp_merge_value; //将拆分的单元格重新赋值(备份值)
}
}
创建DataTable
如果首行是列数据,则以该行的值创建表结构,否则自动创建以“C”为前缀的列名,如C1、C2...Cn以此类推。
System.Data.DataTable dt=ds.Tables.Add();
dt.TableName=worksheet.Name; //表名为worksheet的名称
for(int i=_startcell.Column;i<=_lastcell.Column;i++)
{
Excel.Range _cell=worksheet.Range[worksheet.Cells[_startcell.Row,i],worksheet.Cells[_startcell.Row,i]];
string _colname=hastitle==true?_cell.Value2.ToString():"C"+(i-_startcell.Column+1).ToString(); //如果第一行是标题,则赋单元格的值,否则以C开头加序号
DataColumn dc=dt.Columns.Add();
dc.ColumnName=_colname;
dc.DataType=System.Type.GetType("System.String");
dc.AllowDBNull=true;
}
将单元格数据写入DataTable
object[,] cells=null; 定义二维对象数组
if(hastitle) //如果首行包含列,则加行索引加1取数据行
{
startrow=_startcell.Row+1;
}
//将有效区域单元格转化赋值为 object[,]
cells=(object[,])worksheet.Range[worksheet.Cells[startrow,_startcell.Column],worksheet.Cells[_lastcell.Row,_lastcell.Column]].Value2;
//遍历数组,添加行数据到 DataTable里
int _rowcount=cells.GetLength(0);
int _colcount=cells.GetLength(1);
for(int i=0;i<_rowcount;i++)
{
object[] newrowdata=new object[_colcount];
for(int j=0;j<_colcount;j++)
{
newrowdata[j]=cells[i,j];
}
DataRow dr=dt.Rows.Add(newrowdata);
}
总结
在实际的应用中,还可以设定多种参数选项:
1、如导入单元格的数据,是格式化后的数据(ExcelReport.ImportDataType.FormattingValue),还是原始数据(ExcelReport.ImportDataType.OriginalValue),这也是Cell.Value和Cell.Value2的区别
2、创建表列名字段过度依赖于单元格的值,可能会创建失败,建议定义参数指定是否重写列名
3、是否只导入指定的sheet或活动的sheet。
这些选项都可以根据实际的业务进行扩展,我们在此仅讲述了一些操作Excel相关的关键方法和属性,这里仅作参考,欢迎大家评论指教!
相关文章:

Kubernetes对象的定义和操作
Kubernetes对象指的是Kubernetes系统的持久化实体,所有这些对象合起来,代表了你集群的实际情况。常规的应用里,我们把应用程序的数据存储在数据库中,Kubernetes将其数据以Kubernetes对象的形式通过 api server存储在 etcd 中。集群中运行了哪些容器化应用程序集群中对应用程序可用的资源应用程序相关的策略定义,例如,重启策略、升级策略、容错策略其他Kubernetes管理应用程序时所需要的信息。

使用名称空间共享集群
可以限定使用某个名称空间的用户不能看到另外一个名称空间中的内容。默认情况下,安装Kubernetes集群时,会初始化一个 default 名称空间,用来将承载那些未指定名称空间的 Pod、Service、Deployment等对象。接下来,为 kubectl 定义一个上下文,以便在不同的名称空间中工作。此时,开发人员可以做任何他想要做的操作,所有操作都限定在名称空间 development 里,而无需担心影响到 production 名称空间中的内容。使用 kubectl 有两种方式可以创建名称空间。

k8s 标签和选择器
标签(Label)是附加在Kubernetes对象上的一组名值对,其意图是按照对用户有意义的方式来标识Kubernetes对象,同时,又不对Kubernetes的核心逻辑产生影响。管理这些对象时,很多时候要针对某一个维度的条件做整体操作,例如,将某个版本的程序整体删除,这种情况下,如果用户能够事先规划好标签的使用,再通过标签进行选择,就会非常地便捷。Kubernetes api server支持两种形式的标签选择器,equality-based 基于等式的 和 set-based 基于集合的。

Deployment概述
Pod 容器组是 Kubernetes 中最小的调度单元,更多信息请参考 容器组 - 概述Deployment 是最常用的用于部署无状态服务的方式。Deployment 控制器使得您能够以声明的方式更新 Pod(容器组)和 ReplicaSet(副本集)。以“声明”的方式管理 Pod 和 ReplicaSet,其本质是将一些特定场景的一系列运维步骤固化下来,以便快速准确无误的执行。

k8s搭建部署(超详细)
Kubernetes是Google 2014年创建管理的,是Google 10多年大规模容器管理技术Borg的开源版本。它是容器集群管理系统,是一个开源的平台,可以实现容器集群的自动化部署、自动扩缩容、维护等功能。快速部署应用快速扩展应用无缝对接新的应用功能节省资源,优化硬件资源的使用可移植: 支持公有云,私有云,混合云,多重云(multi-cloud)可扩展: 模块化, 插件化, 可挂载, 可组合自动化: 自动部署,自动重启,自动复制,自动伸缩/扩展。

【OpenCV】在Linux上使用OpenCvSharp
OpenCvSharp是一个OpenCV的 .Net wrapper,应用最新的OpenCV库开发,使用习惯比EmguCV更接近原始的OpenCV,该库采用LGPL发行,对商业应用友好。

k8s图形化管理工具之rancher
在前面的k8s基础学习中,我们学习了各种资源的搭配运用,以及命令行,声明式文件创建。这些都是为了k8s管理员体会k8s的框架,内容基础。在真正的生产环境中,大部分的公司还是会选用图形化管理工具来管理k8s集群,大大提高工作效率。在二进制搭建k8集群时,我们就知道了k8s本身就具有一款原生的k8s集群管理工具,但是原生图形化管理工具dashborad只拥有管理一个集群的能力。而对于现代化生产力公司来讲,一个集群能够做的事情还是太少,所以我们需要引入更强大的集群管理工具。

给服务器开通telnet的流程
但一些特殊场景下,比如要升级ssh,ssh不能用时,需要使用telnet,用过要关闭此服务。需要首先安装,如果telnet-server服务在xinetd之前安装了,要先删除telnet-server,再安装xinetd。安装顺序:xinetd--》telnet--》telnet-server。安装顺序:xinetd--》telnet--》telnet-server。2、卸载rpm包(如果已经安装了,又不清楚顺序,可以都卸载后统一安装)注意:telnet-server服务启动依赖xinetd服务,

k8s 使用tomcat官方镜像部署集群并解决访问页面404
官方镜像这里有个坑,使用kubectl启动之后,页面报错404,仔细检查发现,是因为tomcat的webapp目录下没有对应的文件,所以连初始界面都无法显示。要想显示,必须要根据官方镜像自己构建一个Dockerfile。根据上面的信息可以看出,该POD部署在k8s-node1上,映射POD的8080端口到master的30088端口上。这里需要将镜像上传到自己搭建的registry,并配置nodes节点都可以正常访问5000端口。三、根据官方镜像自己构建一个一次性就能启动的Tomcat镜像。

在C#中调用C++函数并返回const char*类型的值
在C#中,使用DllImport特性将C++函数声明为外部函数。在Main方法中,调用generateProjectCode函数并将返回的指针转换为const char*类型的字符串。在C#中调用C++函数并返回const char*类型的值,可以使用Interop服务来实现。C++代码需要编译为动态链接库(DLL)。

C#winform上位机开发学习笔记3-串口助手的信息保存功能添加
上位机开发的系列学习笔记,避免遗忘多记录多补充多优化

.Net 8.0 Web API Controllers 添加到 windows 服务
但是,如果您希望能够让它托管 API 控制器(也许是为了查看它正在运行的进程的状态),您将需要添加并进行一些更改。要卸载在终端 sc.exe 中运行的服务,请删除“My Worker Service”浏览到http://localhost:5000/my以确保它正在运行。在 Windows 中打开“服务”应用程序,您应该会在那里看到它。(如果更改了 appsettings.json 中的端口,则浏览到。在弹出窗口中,选择“文件夹”,然后按“下一步”、 “完成并关闭”在Program.cs中,您将添加。_.net8.0 服务程序

C# 实现单线程异步互斥锁
C#对异步的支持越来越成熟,async、await简化了代码也提高了可读性,但由于在一段上下文中有了异步操作,意味着这段操作可能会被同时重复调用,如果本身没有被设计可以重复调用的情况下,就很可能会出问题。以上就是今天要讲的内容,本文简单的实现了单线程的异步互斥锁,实现起来相对简单,但作用还是比较大的。虽然说有些情况的异步是可以在前期设计上避免同时调用,比如登录按钮点击后出现蒙板不允许再次点击,但是对于已存在的代码出现了同时调用问题,此时有互斥锁则可以避免大范围改动代码,有效解决问题。_c#实现线程都要经过一个阻塞的方法,线程之间互不干涉

怎么用Office的Excel将图片转为excel表格?
在处理大量的表格数据时,我们经常需要将图片中的表格转换成Excel格式,以便进行更高效的数据分析和处理。2. 打开OCR软件,将需要转换的图片导入。点击“提交识别”,仅识别所点击的图片,点击“识别全部”,将识别列表上的全部图片。第四步:等识别完成后,在弹出的提示框点击“确定”,打开识别结果文件,也可点击取消,然后点击“打开文件”。第二步:点击“图片编辑”,查看图片,如有必要,可进行添加表格线和旋转、裁切图片等操作。点击“打开文件夹”可查看全部已识别的文件,点击下方“选择”,可修改识别文件的保存目录。

一键式Excel分词统计工具:如何轻松打包Python脚本为EXE
最近,表姐遇到了一个挑战:需要从Excel文件中统计出经过分词处理的重复字段,但由于数据隐私问题,这些Excel文件不能外传。这种情况下,直接使用Excel内置功能好像是行不通的,需要借助Python脚本来实现。为了解决这个问题,我写了一个简单的数据分析和自动化办公脚本,以方便使用。想象一下,即使电脑上没有安装Python,也能通过一个简单的EXE文件轻松完成工作,这是多么方便!因此,我决定不仅要写出这个脚本,还要学会如何将其打包成一个独立的EXE文件。这样,无需Python环境的电脑也能直接运行它

【小白专用】C# 连接 MySQL 数据库
C# 连接 MySQL 数据库

RTSP协议播放不兼容TPLINK摄像头的处理办法
报错的内容是Number of element invalid in origin string.两个数字中间多了一个空格,导致判断数据不等于6。所以数据输入的时候把中间的空格去掉一个即可。

即将消失的五种编程语言?
学习路径困难必然导致非常有限的活跃用户,而 Haskell 的上一个最新的稳定版本是在 2010 年发布,这对于促进它本身的发展无济于事。Perl 于 1987 年开始流行时,它被誉为是适合任何一个人的编程语言,曾经有一段时间,每个人都用Perl编程,但是后来发生了一些事情,开发者开始在不知道原因的情况下添加越来越大的功能,也许这增加了了问题的复杂性。甚至它的作者似乎已经含蓄地解释了Perl的一些问题,并选择停止从2000年开始的Perl 6开发,关键是,似乎现在也没人想要在用Perl。

Python实现PDF—>Excel的自动批量转换(附完整代码)
tkinter适用于简单的 GUI 应用,对于入门级开发者和小型项目而言是一个良好的选择。PyQt、PySide、Kivy 和 wxPython 适用于需要更丰富功能、更现代外观或跨平台移动应用的项目,但可能需要更多学习和配置。选择 GUI 库的最佳方法取决于项目的需求、开发者的经验水平以及对不同库的个人偏好。

c#调试程序一次启动两个工程(多个工程)
可以在解决方案中设置多个启动项目(右键单击解决方案,转到设置启动项目,选择多个启动项目),并为包含在解决方案(无开始不调试就开始如果您将多个项目设置为开始,则调试器将在启动时附加到每个项目。

如何通过Python将各种数据写入到Excel工作表
本文中将介绍如何使用Python写入数据到Excel表格,包括文本、数组(List)、XML和CSV数据,提供更高效和准确的Excel表格数据写入方案,从而简化数据处理流程并节省宝贵的时间和精力。

Leetcode算法系列| 11. 盛最多水的容器
给定一个长度为 n 的整数数组 height。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i])。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。说明:你不能倾斜容器。

C#使用 OpenHardwareMonitor获取CPU或显卡温度、使用率、时钟频率相关方式
代码的功能可以将主板的名称显示出来,还有将第一个CPU的情况显示,可以根据实际情况进行修改。C# 去获取电脑相关的基础信息,还是需要借助 外部的库,我这边尝试了自己去实现它。OpenHardwareMonitor获取CPU的温度和频率需要管理员权限。网上有一些信息,但不太完整,都比较零碎,这边尽量将代码完整的去展示出来。引用–>添加引用—>浏览(选择文件)–>确定。代码中注释掉的部分是循环显示的一个循环逻辑。在没有开权限的时候就是无法使用。

C# 读取Word表格到DataSet
在应用项目里,多数情况下我们会遇到导入 Excel 文件数据到数据库的功能需求,但某些情况下,也存在使用 Word 进行表格数据编辑的情况。Word 和 Excel 其实各有特点,但用户的习惯不同,即使同一数据源,可能提供的数据源文件类型也不同,这其中也包括导入Word内容的功能,比如表格数据导出到DataSet数据集。

Python和NetworkX计算有向图节点欧几里德距离最短路径
NetworkX 是一个 Python 语言软件包,用于创建、操作和研究复杂网络的结构、动力学和功能。它用于研究以具有节点和边的图形式表示的大型复杂网络。使用networkx我们可以加载和存储复杂的网络。我们可以生成多种类型的随机和经典网络、分析网络结构、构建网络模型、设计新的网络算法和绘制网络。添加边 (1,2)、(3,1)、(2,4)、(4,1)、(9,1)、(1,7)、(2,9) 后。让我们在图 G 中创建节点。添加节点 1、2、3、4、7、9 后。

C#中var、object和dynamic的区别
在C#中,var、object和dynamic这三个关键字具有不同的特性和用途。var关键字用于隐式类型推断,编译时确定变量类型,一旦确定后不能更改。object关键字是C#中的基础类型,可以保存任意类型的数据,但需要进行装箱和拆箱操作,并且需要显式类型转换才能获取原始数据。dynamic关键字用于动态类型,变量类型可以在运行时推断并更改,避免了显式类型转换的繁琐,但会带来一些性能开销和运行时错误的风险。根据具体的需求和场景,选择合适的关键字进行变量声明和操作是编写高效和可读性良好代码的关键。

文档管理系统的核心技术与难点
概述网上有非常多的“文档管理系统”,随便搜索就能得到超过1000种大大小小的软件或系统,谓之“铺天盖地”也不为过。其中绝大多数是近几年用各类开源的所谓组件、框架搭起来的七拼八凑的产物,其花哨无比的言辞与看似不错的截图,会造成很多用户茫然,掏钱购买后基本上都感觉交了智商税。那么到底什么样的系统才能称为“文档管理系统”呢?怎么选择比较安全呢?先回答第二个问题:世界上任何一个能用的软件至少需要5年的基本成长期。所以,选购的时候,5年以内的软件,就不要考虑了。后面是几个基本概念。文档管理也是各类信息系统

一款跨空间、跨平台、能分享、能搜索常用文件内容、能识别图片文字的全能搜索工具
多可文件快搜安装简单,无需复杂配置。安装在本机后,不仅能搜索本机文件,还可以搜索局域网内共享文件。它可以搜索NAS(SMB协议)上的文件。就连存储在阿里云OSS里的文件,也能轻松搜索到。它还支持IPv6,使用户可以快速安全地搜索网络中的文件。

C/C++,FEISTDLIB的部分源代码
C/C++,FEISTDLIB的部分源代码

C/C++,动态 DP 问题的计算方法与源程序
C/C++,动态 DP 问题的计算方法与源程序