C#调用OpenXml,读取excel行数据,遇到空单元跳过现象处理

问题及现象

在OpenXML中文件不包含空白单元格的条目,这就是跳过空白单元格的原因。

所以如果当我们打开一个excel,读取一个表格数据,发现如果有空单元格,openXML会跳过导致读取的数据发生错位。

比如这个是原始的excel表格数据。

年级班级身份标识姓名性别年龄
一年级 2024010102 15
一年级一班2024010103王五14
一年级一班2024010104赵六14是
一年级一班2024010105钱七14
一年级 2024010106孙八12

当有空格读取后,第一行和最后一行的数据就会错位了,如下:

年级班级身份标识姓名性别年龄
一年级202401010215  
一年级一班2024010103王五14
一年级一班2024010104赵六14是
一年级一班2024010105钱七14
一年级2024010106孙八12 

解决的办法就是:

假设:

DocumentFormat.OpenXml.Spreadsheet.Row row

 Cell cell = row.Elements<Cell>().FirstOrDefault(c => c.CellReference == $"列行");

//例如:c.CellReference =="A12"

当cell是空的时候,表示该单元格是空值。

因此对于从行中提取单元格不能使用“foreach (Cell cell in row)”,这样取出来的cell是非空的单元格,也就是如果你有10列,有两列是空单元格,那么就只能取出来8列,这就导致了取出数据的错位。

而应该使用循环,也就是知道了表格的列数了,然后使用for去循环,例如:

 for (int columnIndex = 0; columnIndex < dt.Columns.Count; columnIndex++){//查找指定的行列单元格是否存在。Cell cell = row.Elements<Cell>().FirstOrDefault(c => c.CellReference == $"{columnReference[columnIndex]}{row.RowIndex}");//例如:c.CellReference =="A12"string cellVal = null;  //定义获取的单元格的值,默认为空if (cell != null){//不为空使用定义的GetCellValue()函数获取cell中的值cellVal = GetCellValue(cell, workbookPart);}....}

以下为封装的OpenXML处理的完整代码

调用ReadSheetWithHeader()函数,ReadSheetWithHeader会调用封装的OpenXml类OutExcel对象,从而把指定的excel文件的sheet表读取到DataTable的数据集合中。

  /// <summary>/// 将指定的excel文件中的指定索引的sheet读取到表对象中/// </summary>/// <param name="fileNm">excel文件路径</param>/// <param name="sheetIndex">sheet索引</param>/// <returns>返回DataTable对象</returns>public DataTable ReadSheetWithHeader(string fileNm, int sheetIndex){FileStream fs = new FileStream(fileNm, FileMode.Open, FileAccess.Read, FileShare.Read);DataTable dt = new OutExcel().ReadExcel(sheetIndex, fs);return dt;}

封装OpenXml类OutExcel

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace OpenExcelMng
{public class OutExcel{/// <summary>/// 按照给定的Excel流组织成Datatable/// </summary>/// <param name="sheetName">须要读取的Sheet的名称</param>/// <param name="stream">Excel文件流</param>/// <returns>组织好的DataTable</returns>public DataTable ReadExcel(string sheetName, Stream stream){using (SpreadsheetDocument document = SpreadsheetDocument.Open(stream, false)){   //打开StreamWorkbookPart workbookPart = document.WorkbookPart;IEnumerable<Sheet> sheets = workbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == sheetName);if (sheets.Count() == 0){//找出合适前提的sheet,没有则返回                     return null;}WorksheetPart worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(sheets.First().Id);//获取Excel中共享数据SharedStringTable stringTable = document.WorkbookPart.SharedStringTablePart.SharedStringTable;IEnumerable<Row> rows = worksheetPart.Worksheet.Descendants<Row>();//获得Excel中得数据行DataTable dt = new DataTable("Excel");//因为须要将数据导入到DataTable中,所以我们假定Excel的第一行是列名,从第二行开端是行数据foreach (Row row in rows){if (row.RowIndex == 1){//Excel第一行动列名GetDataColumn(row, stringTable, ref dt);}else{GetDataRow(row, stringTable, workbookPart, ref dt);//Excel第二行同时为DataTable的第一行数据}}return dt;}}/// <summary>/// 按照给定的Excel流组织成Datatable/// </summary>/// <param name="sheetIndex">须要读取的Sheet的索引</param>/// <param name="sheetIndex">Excel文件流</param>/// <returns>组织好的DataTable</returns>public DataTable ReadExcel(int sheetIndex, Stream stream){using (SpreadsheetDocument document = SpreadsheetDocument.Open(stream, false)){//打开StreamWorkbookPart workbookPart = document.WorkbookPart;IList<Sheet> sheets = workbookPart.Workbook.Descendants<Sheet>().ToList();if (sheets.Count() == 0){//找出合适前提的sheet,没有则返回                     return null;}WorksheetPart worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(sheets[sheetIndex].Id);//获取Excel中共享数据SharedStringTable stringTable = document.WorkbookPart.SharedStringTablePart.SharedStringTable;IEnumerable<Row> rows = worksheetPart.Worksheet.Descendants<Row>();//获得Excel中得数据行DataTable dt = new DataTable("Excel");//因为须要将数据导入到DataTable中,所以我们假定Excel的第一行是列名,从第二行开端是行数据foreach (Row row in rows){if (row.RowIndex == 1){//Excel第一行动列名GetDataColumn(row, stringTable, ref dt);}else{GetDataRow(row, stringTable, workbookPart, ref dt);//Excel第二行同时为DataTable的第一行数据}}return dt;}}/// <summary>/// 构建DataTable的列/// </summary>/// <param name="row">OpenXML定义的Row对象</param>/// <param name="stringTablePart"></param>/// <param name="dt">须要返回的DataTable对象</param>/// <returns></returns>public void GetDataColumn(Row row, SharedStringTable stringTable, ref DataTable dt){DataColumn col = new DataColumn();Dictionary<string, int> columnCount = new Dictionary<string, int>();foreach (Cell cell in row){string cellVal = GetValue(cell, stringTable);col = new DataColumn(cellVal);if (IsContainsColumn(dt, col.ColumnName)){if (!columnCount.ContainsKey(col.ColumnName))columnCount.Add(col.ColumnName, 0);col.ColumnName = col.ColumnName + (columnCount[col.ColumnName]++);}dt.Columns.Add(col);}}/// <summary>/// 构建DataTable的每一行数据,并返回该Datatable/// </summary>/// <param name="row"></param>/// <param name="stringTable"></param>/// <param name="workbookPart">用于处理获取Cell中的信息,如果Cell存在,不是空单元格</param>/// <param name="dt">把行数据写入到datatabl中</param>private void GetDataRow(DocumentFormat.OpenXml.Spreadsheet.Row row,DocumentFormat.OpenXml.Spreadsheet.SharedStringTable stringTable, //不再使用DocumentFormat.OpenXml.Packaging.WorkbookPart workbookPart, //用于处理获取Cell中的信息,如果Cell存在,不是空单元格ref System.Data.DataTable dt)  //把行数据写入到datatabl中。{// 读取算法:按行一一读取单位格,若是整行均是空数据// 则忽视改行(因为本人的工作内容不须要空行)-_-DataRow dr = dt.NewRow();int i = 0;int nullRowCount = i;Dictionary<int, string> columnReference = new Dictionary<int, string>();columnReference.Add(0, "A");columnReference.Add(1, "B");columnReference.Add(2, "C");columnReference.Add(3, "D");columnReference.Add(4, "E");columnReference.Add(5, "F");columnReference.Add(6, "G");columnReference.Add(7, "H");for (int columnIndex = 0; columnIndex < dt.Columns.Count; columnIndex++){Cell cell = row.Elements<Cell>().FirstOrDefault(c => c.CellReference ==     $"{columnReference[columnIndex]}{row.RowIndex}");//例如:c.CellReference =="A12"string cellVal = null;if (cell != null){cellVal = GetCellValue(cell, workbookPart);}if (string.IsNullOrEmpty(cellVal)){nullRowCount++;}dr[i] = cellVal;i++;}           if (nullRowCount != i){dt.Rows.Add(dr);}}/// <summary>/// 获取单位格的值/// </summary>/// <param name="cell">单元格</param>/// <param name="workbookPart"></param>/// <param name="type">1 不去空格 2 前后空格 3 所有空格  </param>/// <returns></returns>public static string GetCellValue(Cell cell, WorkbookPart workbookPart, int type = 2){//合并单元格不做处理if (cell.CellValue == null)return string.Empty;string cellInnerText = cell.CellValue.InnerXml;//纯字符串if (cell.DataType != null && (cell.DataType.Value == CellValues.SharedString || cell.DataType.Value == CellValues.String || cell.DataType.Value == CellValues.Number)){//获取spreadsheetDocument中共享的数据SharedStringTable stringTable = workbookPart.SharedStringTablePart.SharedStringTable;//如果共享字符串表丢失,则说明出了问题。if (!stringTable.Any())return string.Empty;string text = stringTable.ElementAt(int.Parse(cellInnerText)).InnerText;if (type == 2)return text.Trim();else if (type == 3)return text.Replace(" ", "");elsereturn text;}//bool类型else if (cell.DataType != null && cell.DataType.Value == CellValues.Boolean){return (cellInnerText != "0").ToString().ToUpper();}//数字格式代码(numFmtId)小于164是内置的:https://www.it1352.com/736329.htmlelse{//为空为数值if (cell.StyleIndex == null)return cellInnerText;Stylesheet styleSheet = workbookPart.WorkbookStylesPart.Stylesheet;CellFormat cellFormat = (CellFormat)styleSheet.CellFormats.ChildElements[(int)cell.StyleIndex.Value];uint formatId = cellFormat.NumberFormatId.Value;double doubleTime;//OLE 自动化日期值DateTime dateTime;//yyyy/MM/dd HH:mm:ssswitch (formatId){case 0://常规return cellInnerText;case 9://百分比【0%】case 10://百分比【0.00%】case 11://科学计数【1.00E+02】case 12://分数【1/2】return cellInnerText;case 14:doubleTime = double.Parse(cellInnerText);dateTime = DateTime.FromOADate(doubleTime);return dateTime.ToString("yyyy/MM/dd");//case 15://case 16:case 17:doubleTime = double.Parse(cellInnerText);dateTime = DateTime.FromOADate(doubleTime);return dateTime.ToString("yyyy/MM");//case 18://case 19:case 20:doubleTime = double.Parse(cellInnerText);dateTime = DateTime.FromOADate(doubleTime);return dateTime.ToString("H:mm");case 21:doubleTime = double.Parse(cellInnerText);dateTime = DateTime.FromOADate(doubleTime);return dateTime.ToString("HH:mm:ss");case 22:doubleTime = double.Parse(cellInnerText);dateTime = DateTime.FromOADate(doubleTime);return dateTime.ToString("yyyy/MM/dd HH:mm");//case 45://case 46:case 47:doubleTime = double.Parse(cellInnerText);dateTime = DateTime.FromOADate(doubleTime);return dateTime.ToString("yyyy/MM/dd");case 58://【中国】11月11日doubleTime = double.Parse(cellInnerText);dateTime = DateTime.FromOADate(doubleTime);return dateTime.ToString("MM/dd");case 176://【中国】2020年11月11日doubleTime = double.Parse(cellInnerText);dateTime = DateTime.FromOADate(doubleTime);return dateTime.ToString("yyyy/MM/dd");case 177://【中国】11:22:00doubleTime = double.Parse(cellInnerText);dateTime = DateTime.FromOADate(doubleTime);return dateTime.ToString("HH:mm:ss");default:return cellInnerText;}}}/// <summary>/// 获取单位格的值/// </summary>/// <param name="cell"></param>/// <param name="stringTablePart"></param>/// <returns></returns>private string GetValue(Cell cell, SharedStringTable stringTable){//因为Excel的数据存储在SharedStringTable中,须要获取数据在SharedStringTable 中的索引string value = string.Empty;try{if (cell.ChildElements.Count == 0)return value;value = double.Parse(cell.CellValue.InnerText).ToString();if (cell.DataType != null){switch (cell.DataType.Value){case CellValues.SharedString:value = stringTable.ChildElements[Int32.Parse(value)].InnerText; break;}}}catch (Exception ex){value = "N/A";}return value;}/// <summary>/// 判断网格是否存在列/// </summary>/// <param name="dt">网格</param>/// <param name="columnName">列名</param>/// <returns></returns>public bool IsContainsColumn(DataTable dt, string columnName){if (dt == null || columnName == null){return false;}return dt.Columns.Contains(columnName);}public static void ConvertToDateTime(ref DataTable dt, string columnNm, string dtFormat){int findLoca_Old = dt.Columns.IndexOf(columnNm);DataColumn newColumn = new DataColumn(System.Guid.NewGuid().ToString(), typeof(String));string newColumnNm = newColumn.ColumnName;dt.Columns.Add(newColumn);newColumn.SetOrdinal(findLoca_Old + 1);foreach (DataRow row in dt.Rows){try{double val = Convert.ToDouble(row[columnNm]);row[newColumnNm] = DateTime.FromOADate(val).ToString(dtFormat);}catch (Exception ex){;}}dt.Columns.RemoveAt(findLoca_Old);newColumn.ColumnName = columnNm;}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/495515.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分布式协同 - 分布式事务_2PC 3PC解决方案

文章目录 导图Pre2PC&#xff08;Two-Phase Commit&#xff09;协议准备阶段提交阶段情况 1&#xff1a;只要有一个事务参与者反馈未就绪&#xff08;no ready&#xff09;&#xff0c;事务协调者就会回滚事务情况 2&#xff1a;当所有事务参与者均反馈就绪&#xff08;ready&a…

GFPS扩展技术原理(七)-音频切换消息流

音频切换消息流 Seeker和Provider通过消息流来同步音频切换能力&#xff0c;触发连接做切换&#xff0c;获取或设置音频切换偏好&#xff0c;通知连接状态等等。为此专门定义了音频切换消息流Message Group 为0x07&#xff0c;Message codes如下&#xff1a; MAC of Audio s…

实现 QTreeWidget 中子节点勾选状态的递归更新功能只影响跟节点的状态父节点状态不受影响

在 Qt 开发中&#xff0c;QTreeWidget 提供了树形结构的显示和交互功能。为了实现某个子节点勾选或取消勾选时&#xff0c;只影响当前节点及其子节点的状态&#xff0c;同时递归更新父节点的状态以正确显示 Qt::PartiallyChecked 或 Qt::Checked&#xff0c;我们可以借助 Qt 的…

计算机图形学知识点汇总

一、计算机图形学定义与内容 1.图形 图形分为“图”和“形”两部分。 其中&#xff0c;“形”指形体或形状&#xff0c;存在于客观世界和虚拟世界&#xff0c;它的本质是“表示”&#xff1b;而图则是包含几何信息与属性信息的点、线等基本图元构成的画面&#xff0c;用于表达…

Yolo11改策略:卷积改进|SAC,提升模型对小目标和遮挡目标的检测性能|即插即用

摘要 一、论文介绍 本文参考的论文主要介绍了DetectoRS模型&#xff0c;一个高性能的目标检测模型。DetectoRS通过引入递归特征金字塔&#xff08;RFP&#xff09;和可切换空洞卷积&#xff08;SAC&#xff09;两大创新点&#xff0c;显著提升了目标检测的精度。尽管原文并未…

Y3编辑器教程8:资源管理器与存档、防作弊设置

文章目录 一、资源管理器简介1.1 界面介绍1.2 资源商店1.3 AI专区1.3.1 AI文生图1.3.2 AI图生图1.3.3 立绘头像 二、导入导出2.1 文件格式2.2 模型导入2.2.1 模型制作后导出2.2.2 模型文件导入Y3编辑器2.2.3 Y3编辑器角色、装饰物模型要求 2.3 纹理导入2.4 材质贴图2.4.1 材质支…

【联动】【MSS】【AF】

【联动】【MSS】【AF】 一、版本要求 AF&#xff1a;不低于8.0.7&#xff1b;AF8.0.7R2不支持接入 二、接入配置 2.1、AF配置 对于AF8.0.13版本及以上&#xff0c;登录WEB控制台&#xff0c;点击[下一代安全防护体系] -> [云网联动] -> [云网接入设置]&#xff0c;然…

攻防世界 cookie

开启场景 Cookie&#xff08;HTTP cookie&#xff09;是一种存储在用户计算机上的小型文本文件。它由网站通过用户的浏览器在用户访问网站时创建&#xff0c;并存储一些用于跟踪和识别用户的信息。Cookie 主要用于在网站和浏览器之间传递数据&#xff0c;以便网站可以根据用户的…

STM32-笔记11-手写带操作系统的延时函数

1、为什么带操作系统的延时函数&#xff0c;和笔记10上的延时函数不能使用同一种&#xff1f; 因为笔记10的延时函数在每次调用的时候&#xff0c;会一直开关定时器&#xff0c;而在FreeRTOS操作系统中&#xff0c;SysTick定时器当作时基使用。 时基是一个时间显示的基本单位。…

JWT令牌与微服务

1. 什么是JWT JWT&#xff08;JSON Web Token&#xff09;是一种开放标准(RFC 7519)&#xff0c;它定义了一种紧凑且自包含的方式&#xff0c;用于作为JSON对象在各方之间安全地传输信息。JWT通常用于身份验证和信息交换。 以下是JWT的一些关键特性&#xff1a; 紧凑&#xff…

代码随想录Day37 动态规划:完全背包理论基础,518.零钱兑换II,本周小结动态规划,377. 组合总和 Ⅳ,70. 爬楼梯(进阶版)。

1.完全背包理论基础 思路 完全背包 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09;&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 完…

【多时段】含sop的配电网重构【含分布式电源】【已更新视频讲解】

1 主要内容 之前分享了很多配电网重构的程序&#xff0c;每个程序针对场景限定性比较大&#xff0c;程序初学者修改起来难度较大&#xff0c;本次分享一个基础程序&#xff0c;针对含sop的配电网重构模型&#xff0c;含风电和光伏&#xff0c;优化了33节点网络电压合理性&…

查看php已安装扩展命令

在powershell中查看完整的拓展 php -m 指定搜索某几个拓展 php -m | Select-String -Pattern "xml"

YOLOv11 引入高效的可变形卷积网络 DCNv4 | 重新思考用于视觉应用的动态和稀疏算子

我们介绍了可变形卷积v4(DCNv4),这是一种为广泛的视觉应用设计的高效且有效的算子。DCNv4通过以下两项关键改进解决了其前身DCNv3的局限性: 在空间聚合中移除softmax归一化,以增强其动态特性和表达能力。优化内存访问,减少冗余操作以提高速度。这些改进使得DCNv4相比DCNv…

vue 基础学习

一、ref 和reactive 区别 问题&#xff1a;发生跨域问题 Access to script at file:///Users/new/Desktop/webroot/vue/vue.esm-browser.js from origin null has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: chrome, chrom…

AIA - IMSIC之二(附IMSIC处理流程图)

本文属于《 RISC-V指令集基础系列教程》之一,欢迎查看其它文章。 1 ​​​​​​​通过IMSIC接收外部中断的CSR 软件通过《AIA - 新增的CSR》描述的CSR来访问IMSIC。 machine level 的 CSR 与 IMSIC 的 machine level interrupt file 可相互互动;而 supervisor level 的 CSR…

光谱相机的工作原理

光谱相机的工作原理主要基于不同物质对不同波长光的吸收、反射和透射特性存在差异&#xff0c;以下是其具体工作过程&#xff1a; 一、光的收集 目标物体在光源照射下&#xff0c;其表面会对光产生吸收、反射和透射等相互作用。光谱相机的光学系统&#xff08;如透镜、反射镜…

Kafka可视化工具 Offset Explorer (以前叫Kafka Tool)

数据的存储是基于 主题&#xff08;Topic&#xff09; 和 分区&#xff08;Partition&#xff09; 的 Kafka是一个高可靠性的分布式消息系统&#xff0c;广泛应用于大规模数据处理和实时, 为了更方便地管理和监控Kafka集群&#xff0c;开发人员和运维人员经常需要使用可视化工具…

TLDR:终端命令的简洁百科全书

TLDR&#xff0c;全称 “Too Long, Don’t Read”&#xff0c;是一款特别实用的终端命令百科全书工具。通过 TLDR&#xff0c;您可以快速查找到常用命令的使用方法&#xff0c;避免繁琐冗长的官方文档&#xff0c;让日常工作更加高效。 为什么选择 TLDR&#xff1f; 简单易用&…

2024-12-25-sklearn学习(20)无监督学习-双聚类 料峭春风吹酒醒,微冷,山头斜照却相迎。

文章目录 sklearn学习(20) 无监督学习-双聚类1 Spectral Co-Clustering1.1 数学公式 2 Spectral Biclustering2.1 数学表示 3 Biclustering 评价 sklearn学习(20) 无监督学习-双聚类 文章参考网站&#xff1a; https://sklearn.apachecn.org/ 和 https://scikit-learn.org/sta…