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))
            {   //打开Stream
                WorkbookPart 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))
            {//打开Stream
                WorkbookPart 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(" ", "");
                else
                    return text;
            }
            //bool类型
            else if (cell.DataType != null && cell.DataType.Value == CellValues.Boolean)
            {
                return (cellInnerText != "0").ToString().ToUpper();
            }
            //数字格式代码(numFmtId)小于164是内置的:https://www.it1352.com/736329.html
            else
            {
                //为空为数值
                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:ss
                switch (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:00
                        doubleTime = 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;
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

x86_64 Ubuntu 编译安装英伟达GPU版本的OpenCV

手把手带你在Linux上安装带GPU加速的opencv库&#xff08;C版本&#xff09;_opencv linux-CSDN博客 cmake \-D CMAKE_BUILD_TYPERELEASE \-D OPENCV_GENERATE_PKGCONFIGON \-D CMAKE_INSTALL_PREFIX/usr/local \-D OPENCV_EXTRA_MODULES_PATH/home/hwj/opencv/opencv_contrib…

Bert各种变体——RoBERTA/ALBERT/DistillBert

RoBERTa 会重复一个语句10次&#xff0c;然后每次都mask不同的15%token。丢弃了NSP任务&#xff0c;论文指出NSP任务有时甚至会损害性能。使用了BPE ALBERT 1. 跨层参数共享 可以共享多头注意力层的参数&#xff0c;或者前馈网络层的参数&#xff0c;或者全部共享。 实验结果…

ReMoE: Fully Differentiable Mixture-of-Experts with ReLU Routing

基本信息 &#x1f4dd; 原文链接: https://arxiv.org/abs/2412.14711&#x1f465; 作者: Ziteng Wang, Jianfei Chen, Jun Zhu&#x1f3f7;️ 关键词: Mixture-of-Experts, ReLU routing&#x1f4da; 分类: 机器学习 摘要 中文摘要 稀疏激活的专家混合模型&#xff08;…

【C语言程序设计——选择结构程序设计】预测你的身高(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 1、输入数值 2、选择结构语句 3、计算结果并输出 编程要求 测试说明 通关代码 测试结果 任务描述 本关任务&#xff1a;编写一个程序&#xff0c;该程序需输入个人数据&#xff0c;进而预测其成年后的身高。 相关知识 为了完成本…

(Arxiv-2024)SwiftEdit:通过一步扩散实现闪电般快速的文本引导图像编辑

SwiftEdit&#xff1a;通过一步扩散实现闪电般快速的文本引导图像编辑 Paper是VinAI Research发表在Arxiv2024的工作 Paper Title:SwiftEdit: Lightning Fast Text-Guided Image Editing via One-Step Diffusion Code地址 Abstract 文本引导的图像编辑方面的最新进展利用了基于…

python langid识别一段字符串是哪国语言

分析&#xff1a; 在利用爬虫抓取亚马逊网站的数据时&#xff0c;有时会出现所抓页面的语言类型发生错误的情况&#xff08;如抓取沙特站数据时想要英文页面&#xff0c;抓到的确是阿拉伯语页面&#xff09;。在数据量大的时候人工排查这类异常情况是非常麻烦的&#xff0c;这时…

英特尔的创新困局与未来的转机:重塑还是消亡?

英特尔&#xff0c;这家曾引领全球半导体行业的巨头&#xff0c;如今正面临前所未有的挑战。从技术创新的停滞&#xff0c;到错失人工智能领域的制高点&#xff0c;再到被AMD和英伟达等竞争对手赶超&#xff0c;英特尔的创新之路似乎正走向尽头。但这是否意味着它的未来注定黯淡…

软考:系统架构设计师教材笔记(持续更新中)

教材中的知识点都会在。其实就是将教材中的废话删除&#xff0c;语言精练一下&#xff0c;内容比较多&#xff0c;没有标注重点 系统架构概述 定义 系统是指完成某一特定功能或一组功能所需要的组件集&#xff0c;而系统架构则是对所有组件的高层次结构表示&#xff0c;包括各…

No.1免费开源ERP:Odoo自定义字段添加到配置页中的技术分享

文 / 开源智造&#xff08;OSCG&#xff09; Odoo亚太金牌服务 在Odoo18之中&#xff0c;配置设定于管控各类系统配置层面发挥着关键之效用&#xff0c;使您能够对软件予以定制&#xff0c;以契合您特定的业务需求。尽管 Odoo 提供了一组强劲的默认配置选项&#xff0c;然而有…

YOLO11全解析:从原理到实战,全流程体验下一代目标检测

前言 一、模型介绍 二、网络结构 1.主干网络&#xff08;Backbone&#xff09; 2.颈部网络&#xff08;Neck&#xff09; 3.头部网络&#xff08;Head&#xff09; 三、算法改进 1.增强的特征提取 2.优化的效率和速度 3.更高的准确性与更少的参数 4.环境适应性强 5.…

虚幻引擎结构之ULevel

在虚幻引擎中&#xff0c;场景的组织和管理是通过子关卡&#xff08;Sublevel&#xff09;来实现的。这种设计不仅提高了资源管理的灵活性&#xff0c;还优化了游戏性能&#xff0c;特别是在处理大型复杂场景时。 1. 场景划分模式 虚幻引擎采用基于子关卡的场景划分模式。每个…

自动驾驶---Parking端到端架构

​​​​​​1 背景 自动泊车也是智能驾驶低速功能中比较重要的一部分&#xff0c;低速功能其中还包括记忆泊车&#xff0c;代客泊车等。传统的泊车算法通常使用基于规则或者搜索优化的方案来实现。然而&#xff0c;由于算法的复杂设计&#xff0c;这些方法在复杂的泊车场景中效…

[ffmpeg]编译 libx264

步骤 下载 libx264 git clone https://code.videolan.org/videolan/x264.git cd x264环境搭建 然后在开始菜单中找到并打开 x64 Native Tools Command Prompt for VS 2019 &#xff1a; 打开 msys2_shell.cmd -use-full-path 这时会打开 MSYS 的新窗口&#xff0c;先把一些汇…

华为管理变革之道:管理制度创新

目录 华为崛起两大因素&#xff1a;管理制度创新和组织文化。 管理是科学&#xff0c;150年来管理史上最伟大的创新是流程 为什么要变革&#xff1f; 向世界标杆学习&#xff0c;是变革第一方法论 体系之一&#xff1a;华为的DSTE战略管理体系&#xff08;解决&#xff1a…

【自留】Unity VR入门

帮老师写的&#xff0c;自留&#xff0c;不保证是很好的教程。 1.PICO开发指南&#xff08;官方&#xff09; 在该页面&#xff0c;能找到大部分能实现的功能&#xff0c;以及实现方式。非常推荐&#xff01;PICO Unity Integration SDK | PICO 开发者平台 2.如何快速入门&…

uniapp 项目基础搭建(vue2)

一 .创建项目 创建项目可以通过工具创建&#xff0c;也可以通过脚手架下载 1.通过工具创建 2.通过脚手架下载 安装脚手架 ​​npm install -g vue/cli 下载项目模板 vue create -p dcloudio/uni-preset-vue 项目名称 二. 下载相关依赖 1. 项目默认是没有package.json文件的&…

使用vcpkg安装opencv>=4.9后#include<opencv2/opencv.hpp>#include<opencv2/core.hpp>无效

使用vcpkg安装opencv>4.9后#include<opencv2/opencv.hpp>#include<opencv2/core.hpp>无效\无法查找或打开 至少从2024年开始&#xff0c;发布的vcpkg默认安装的opencv版本都是4.x版。4.8版本及以前&#xff0c;vcpkg编译后的opencv头文件目录是*/vcpkg/x64-win…

Kubernetes PV及PVC的使用

前提条件 拥有Kubernetes集群环境&#xff0c;可参考&#xff1a;Kubernetes集群搭建理解Kubernetes部署知识&#xff0c;可参考&#xff1a;使用Kubernetes部署第一个应用 、Deloyment控制器拥有NFS服务&#xff0c;可参考&#xff1a;Linux环境搭建NFS服务 概述 Persistent…

flink sink kafka

接上文&#xff1a;一文说清flink从编码到部署上线 之前写了kafka source&#xff0c;现在补充kafka sink。完善kafka相关操作。 环境说明&#xff1a;MySQL&#xff1a;5.7&#xff1b;flink&#xff1a;1.14.0&#xff1b;hadoop&#xff1a;3.0.0&#xff1b;操作系统&#…

【安全编码】Web平台如何设计防止重放攻击

我们先来做一道关于防重放的题&#xff0c;答案在文末 防止重放攻击最有效的方法是&#xff08; &#xff09;。 A.对用户密码进行加密存储使用 B.使用一次一密的加密方式 C.强制用户经常修改用户密码 D.强制用户设置复杂度高的密码 如果这道题目自己拿不准&#xff0c;或者…