基础类库积累--ExeclHelper类
前言:
相信大家都玩过NPOI这个第三方组件,我就分享一下我平时使用的工具类,如果有不好的地方,请赐教!
NPOI是什么?
NPOI是一个开源的C#读写Excel、WORD等微软OLE2组件文档的项目。
NPOI怎么安装?
NuGet:
控制台:
命令:
Install-Package NPOI
输入命令之后,回车即安装
NPOI怎么使用?
安装NPOI之后,程序中就已经把NPOI服务集成到我们程序了,我们现在来建立一个帮助类,编写读取Execl和导出Execl。我这里的读取Execl,把每一个Sheet页当做一个DataTable,多个DataTable组成一个DataSet,然后将DataSet返回。
NPOI读取Execl
/// <summary>
/// Excel导入成DataTble
/// </summary>
/// <param name="file">导入路径(包含文件名与扩展名)</param>
/// <returns></returns>
public static DataSet ExcelToTable(string file, ref List<string> list_sheetName)
{
DataSet ds = new DataSet();
IWorkbook workbook;
string fileExt = Path.GetExtension(file).ToLower();
using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read))
{
if (fileExt == ".xlsx") { workbook = new XSSFWorkbook(fs); } else if (fileExt == ".xls") { workbook = new HSSFWorkbook(fs); } else { workbook = null; }
if (workbook == null) { return null; }
for (int k = 0; k < workbook.NumberOfSheets; k++)
{
DataTable dt = new DataTable();
ISheet sheet = workbook.GetSheetAt(k);
list_sheetName.Add(sheet.SheetName);
//表头
IRow header = sheet.GetRow(sheet.FirstRowNum);
//过滤空的Sheet
if (header!=null)
{
List<int> columns = new List<int>();
for (int i = 0; i < header.LastCellNum; i++)
{
object obj = GetValueType(header.GetCell(i));
if (obj == null || obj.ToString() == string.Empty)
{
dt.Columns.Add(new DataColumn("Columns" + i.ToString()));
}
else
dt.Columns.Add(new DataColumn(obj.ToString()));
columns.Add(i);
}
dt.Columns.Add(new DataColumn("SheetName"));
//数据
for (int i = sheet.FirstRowNum + 1; i <= sheet.LastRowNum; i++)
{
DataRow dr = dt.NewRow();
bool hasValue = false;
foreach (int j in columns)
{
if (sheet.GetRow(i) != null)
{
dr[j] = GetValueType(sheet.GetRow(i).GetCell(j));
if (dr[j] != null && dr[j].ToString() != string.Empty)
{
hasValue = true;
}
}
}
if (hasValue)
{
dr[columns.Count] = sheet.SheetName;
dt.Rows.Add(dr);
}
}
ds.Tables.Add(dt);
}
}
}
return ds;
}
/// <summary>
/// 获取单元格类型
/// </summary>
/// <param name="cell">目标单元格</param>
/// <returns></returns>
private static object GetValueType(ICell cell)
{
if (cell == null)
return null;
switch (cell.CellType)
{
case CellType.Blank:
return null;
case CellType.Boolean:
return cell.BooleanCellValue;
case CellType.Numeric:
return cell.NumericCellValue;
case CellType.String:
return cell.StringCellValue;
case CellType.Error:
return cell.ErrorCellValue;
case CellType.Formula:
default:
return "=" + cell.CellFormula;
}
}
思考?
我这里读取之后是一个DataSet集合,但是这种数据集虽然在结构上很清晰,一个DataTable对应一个Sheet,但是处理器数据其他麻烦(比如,我想查询表中Name为"张三"的用户信息,肯定是不好查询的),还是就是如果在每个Sheet数据格式相同的情况下,肯定会有想把它们整合在一起的想法,那该如何整合在一起?
思路:
要是能转换为List数组就好,我们就能使用Linq和Lambda进行数据的快速处理。如何把DataSet转换为List,我们可以观察execl中的数据,然后对应在项目中建立一个Model类,用英文做字段,用DisplayName标识对应的中文(为什么要这样,后面会讲),建立一个List,现在只要把DataSet中的DataTable取出来,然后利用反射的方式,比较DataTable中的列名和Model中对应的DisplayName,如何一样,,则存储到List,这里存在一个DataTable转换为List。
Execl数据:
Mode类:
只要这样一一对应起来,后期委会也好维护,比如新增了一个列,在Model中加一个字段即可,方便扩展,如果是多个Sheet,每个Sheet略有不同,就可以使用c#面向对象的思想,提取它们共同的字段,其他做继承,这里就不多说了。接下来讲讲如何做DataSet转换为List,其实DataSet中就是好多DataTable组成,如果能实现DataTable转换到List,其他就迎刃而解!
DataTable转换List
public static class DataTableToList
{
/// <summary>
/// DataTable转成List
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dt"></param>
/// <returns></returns>
public static List<T> ToDataList<T>(this DataTable dt)
{
var list = new List<T>();
var plist = new List<PropertyInfo>(typeof(T).GetProperties());
foreach (DataRow item in dt.Rows)
{
T s = Activator.CreateInstance<T>();
for (int i = 0; i < dt.Columns.Count; i++)
{
PropertyInfo info = plist.Find(p => p.GetCustomAttribute<System.ComponentModel.DisplayNameAttribute>().DisplayName == dt.Columns[i].ColumnName);
if (info != null)
{
try
{
if (!Convert.IsDBNull(item[i]))
{
object v = null;
if (info.PropertyType.ToString().Contains("System.Nullable"))
{
v = Convert.ChangeType(item[i], Nullable.GetUnderlyingType(info.PropertyType));
}
else
{
v = Convert.ChangeType(item[i], info.PropertyType);
}
info.SetValue(s, v, null);
}
}
catch (Exception ex)
{
throw new Exception("字段[" + info.Name + "]转换出错," + ex.Message);
}
}
}
list.Add(s);
}
return list;
}
public static List<T> ToDataSetList<T>(this DataSet ds)
{
var list = new List<T>();
for (int i = 0; i < ds.Tables.Count; i++)
{
list = list.Concat(ToDataList<T>(ds.Tables[i])).ToList();
}
return list;
}
}
这里是使用泛型+反射的技术,对DataTable转换为List进行封装,只要你的格式一致(Execl和Model),就可以实现转换。
导出Execl
可以导入Execl,然后转换为List之后,我们可以为所欲为了,但是修改数据以后,我们可以想保存信息到Execl。
思考?
- 如何导出List到一个新的Execl?
思路:
- List这个思路很简单,第一步创建一个IWorkbook(Execl对象),第二部创建Sheet,起个名字,然后把List数据遍历到Sheet中,最后写入到文件中。
/// <summary>
/// List<T>导出Execl
/// </summary>
/// <typeparam name="T">模型类</typeparam>
/// <param name="file">保存文件的路径</param>
/// <param name="list">需要保存的数据</param>
public static void ListToExecl<T>(string file, List<T> list)
{
IWorkbook workbook;
string fileExt = Path.GetExtension(file).ToLower();
if (fileExt == ".xlsx") { workbook = new XSSFWorkbook(); } else if (fileExt == ".xls") { workbook = new HSSFWorkbook(); } else { workbook = null; }
if (workbook == null) { return; }
//中文显示的列名
string DisplayName = string.Empty;
ISheet sheet = workbook.CreateSheet();
//表头
IRow header = sheet.CreateRow(0);
Type t = typeof(T);
PropertyInfo[] properties = t.GetProperties();
int index = 0;
foreach (PropertyInfo field in properties)
{
DynamicGetProperty(list[0], field.Name, ref DisplayName).ToString();
header.CreateCell(index).SetCellValue(DisplayName);
index += 1;
}
for (int i = 0; i < list.Count; i++)
{
index = 0;
header = sheet.CreateRow(1+i);
foreach (PropertyInfo field in properties)
{
string name = DynamicGetProperty(list[i], field.Name, ref DisplayName).ToString();
header.CreateCell(index).SetCellValue(name);
index += 1;
}
}
using (FileStream fs=new FileStream(file,FileMode.Create,FileAccess.ReadWrite))
{
workbook.Write(fs);
}
}
/// <summary>
/// 动态获取对象的属性
/// </summary>
/// <param name="obj">传入的对象</param>
/// <param name="propName">属性名</param>
/// <returns></returns>
public static object DynamicGetProperty(object obj, string propName,ref string DisplayName)
{
// TODO: 检查属性名合法性
var propNames = propName.Split('.');
var val = obj;
foreach (var prop in propNames)
{
var propInfo = val.GetType().GetProperty(prop);
DisplayName = propInfo.GetCustomAttribute<DisplayNameAttribute>().DisplayName;
val = propInfo.GetValue(val);
}
return val;
}
总结:
我在这只是抛砖引玉,其实NPOI还有一些其他东西, 大家可以自行研究,比如:DataTable导出Exelc,C# NPOI计算Execl里面的公式等等。
- 肯定有人会有疑问,我为什么要把Execl先转换为DataSet,在去转换为List,为什么不在一开始就去转换为List?
- 答:第一我们不知道Execl数据的有多少Sheet,如果针对每一个都去写一个规则,繁琐且麻烦。你按照我的这种方式,不管你有多少Sheet,只要我知道你的格式【列名】,我就都可以转换为List,虽然多了一层转换,但是我逻辑清晰,代码复用率高,针对不同的Sheet编写对应的模型就可以,并不需要我每次去编写特定的格式。
- 有人还是有疑问,你这导出怎么就List直接转换到一个Sheet中,如果我想分开,之前怎么读取,之后就怎么保存,我该如何做?
- 答:其实这个也挺简单,我没做扩展,你在使用我代码的时候,一定会发现,List中多了一列值【SheetName】,所以你在保存的时候,读SheetName进行分类,然后遍历保存即可。
原文地址:https://www.cnblogs.com/2828sea/p/13493710.html
基础类库积累--ExeclHelper类的更多相关文章
- 菜鸡的Java笔记 java基础类库 BaseClassLibrary
java基础类库 BaseClassLibrary StringBuffer 类的特点 StringBuffer,StringBuilder,String 类之间的关系 ...
- Java以基础类库
Java以基础类库JFC(Java Foundation Class)的形式为程序员提供编程接口API,类库中的类按照用途归属于不同的包中. (一)java.lang包 Java最常用的包都属于该包, ...
- MFC - 微软基础类库和框架
一 MFC的概念和作用 1 什么是MFC?? 全称 Microsoft Foundation Class Library我们称之为微软基础类库 1)从硬盘的存在形式上来说 MFC就是一个库(静/动态库 ...
- Yaf零基础学习总结5-Yaf类的自动加载
Yaf零基础学习总结5-Yaf类的自动加载 框架的一个重要功能就是类的自动加载了,在第一个demo的时候我们就约定自己的项目的目录结构,框架就基于这个目录结构来自动加载需要的类文件. Yaf在自启动的 ...
- Java中基础类库使用
Java中基础类库: 在这里我仅仅介绍几种我个人觉得会常常使用的 1:Object类中的Clone机制仅仅是对对象进行浅层次的克隆,假设须要进行深层次的克隆的话那么就要自己写(详细Clone方法请參考 ...
- Sangmado 公共基础类库
Sangmado 涵盖了支撑 .NET/C# 项目开发的最基础的公共类库,为团队在不断的系统开发和演进过程中发现和积累的最公共的代码可复用单元. Sangmado 公共类库设计原则: 独立性:不与任何 ...
- Java基础类库简介
Java基础类库简介 一.常用的基础类库:11个jar(Java Archive,Java归档)包 作为java语言使用者,我们可以感受到java语言带来的优势(平台无关.面向对象.多线程.高效易扩展 ...
- Java核心技术梳理-基础类库
一.引言 Oracle为Java提供了丰富的基础类库,Java 8 提供了4000多个基础类库,熟练掌握这些基础类库可以提高我们的开发效率,当然,记住所有的API是不可能也没必要的,我们可以通过API ...
- Java知多少(74)基础类库
Java 的类库是 Java 语言提供的已经实现的标准类的集合,是 Java 编程的 API(Application Program Interface),它可以帮助开发者方便.快捷地开发 Java ...
随机推荐
- 死磕Spring源码之AliasRegistry
死磕Spring源码之AliasRegistry 父子关系 graph TD; AliasRegistry-->BeanDefinitionRegistry; 代码实现 作为bean定义的最顶层 ...
- js获得url地址携带参数
function GetQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&] ...
- css的一些小技巧。修改input样式
在第一次正式写项目的时候,遇到了几个布局的小技巧.记录一下. 我们常常会遇到图片和文字对齐的一种样式.比如 这样的样式,我们写的时候有时候会出现不对齐的情况.我们有俩种方法 一种就是flex的布局,还 ...
- Python数据类型-str,list常见操作
一.字符串操作 语法:字符串名.startwith('字符串') 功能:判断字符串里是否以xxx开头 范例: 扩展:从控制台接收输入居住地址,如果地址以北京市开头,则输出北京人口,否则输入非北京人口. ...
- kafka笔记——入门介绍
中文文档 目录 kafka的优势 首先几个概念 kafka的四大核心API kafka的基本术语 主题和日志(Topic和Log) 每个分区都是一个顺序的,不可变的队列,并且可以持续的添加,分区中的每 ...
- Html5 表单元素基础
表单元素 1.定义: 表单是提供让读者在网页上输入,勾选和选取数据,以便提交给服务器数据库的工具.(邮箱注册,用户登录,调查问卷等) 2.表单元素(下拉框,输入框……) 3.表单主结构: <fo ...
- PHP array_udiff_assoc() 函数
实例 比较两个数组的键名和键值(使用内建函数比较键名,使用用户自定义函数比较键值),并返回差集: <?phpfunction myfunction($a,$b){if ($a===$b){ret ...
- PHP fileperms() 函数
定义和用法 fileperms() 函数返回文件或目录的权限. 如果成功,该函数以数字形式返回权限.如果失败,则返回 FALSE. 语法 fileperms(filename) 参数 描述 filen ...
- PDO::inTransaction
PDO::inTransaction — 检查是否在一个事务内(PHP 5 >= 5.3.3, Bundled pdo_pgsql) 说明 语法 bool PDO::inTransaction ...
- Latex—参考文献
在写文章的最后最让我头疼的就是参考文献的问题了.网上的资料也有很多,这里整合了很多资料得出了一个用bib文件的方法. 1. 显示确定参考文献(一句没什么用的废话). 2. 利用谷歌学术(镜像),如 ...