C# 中 NPOI 库读写 Excel 文件的方法【摘】
原作:淡水网志
NPOI 是开源的 POI 项目的.NET版,可以用来读写Excel,Word,PPT文件。在处理Excel文件上,NPOI 可以同时兼容 xls 和 xlsx。官网提供了一份 Examples,给出了很多应用场景的例子,打包好的二进制文件类库,也仅有几MB,使用非常方便。
读Excel
NPOI使用HSSFWorkbook
类来处理xls,XSSFWorkbook
类来处理xlsx,它们都继承接口IWorkbook
,因此可以通过IWorkbook
来统一处理xls和xlsx格式的文件。
以下是简单的例子
1 /// <summary> 2 /// 读取excel文件 3 /// </summary> 4 /// <param name="filePath">文件路径</param> 5 public void ReadFromExcelFile(string filePath) 6 { 7 IWorkbook wk = null; 8 string extension = System.IO.Path.GetExtension(filePath); 9 try 10 { 11 FileStream fs = File.OpenRead(filePath); 12 if (extension.Equals(".xls")) 13 { 14 //把xls文件中的数据写入wk中 15 wk = new HSSFWorkbook(fs); 16 } 17 else 18 { 19 //把xlsx文件中的数据写入wk中 20 wk = new XSSFWorkbook(fs); 21 } 22 23 fs.Close(); 24 //读取当前表数据 25 ISheet sheet = wk.GetSheetAt(0); 26 27 IRow row = sheet.GetRow(0); //读取当前行数据 28 //LastRowNum 是当前表的总行数-1(注意) 29 30 string text = string.Empty; 31 for (int i = 0; i <= sheet.LastRowNum; i++) 32 { 33 row = sheet.GetRow(i); //读取当前行数据 34 if (row != null) 35 { 36 //LastCellNum 是当前行的总列数 37 for (int j = 0; j < row.LastCellNum; j++) 38 { 39 //读取该行的第j列数据 40 string value = row.GetCell(j).ToString(); 41 //Console.Write(value.ToString() + " "); 42 text = text + value.ToString() + "\r\n"; 43 } 44 //Console.WriteLine("\n"); 45 } 46 } 47 SetText(text); 48 } 49 50 catch (Exception e) 51 { 52 //只在Debug模式下才输出 53 //Console.WriteLine(e.Message); 54 this.textBox1.Text = e.Message; 55 } 56 }
Excel中的单元格是有不同数据格式的,例如数字,日期,字符串等,在读取的时候可以根据格式的不同设置对象的不同类型,方便后期的数据处理。
1 //获取cell的数据,并设置为对应的数据类型 2 public object GetCellValue(ICell cell) 3 { 4 object value = null; 5 try 6 { 7 if (cell.CellType != CellType.Blank) 8 { 9 switch (cell.CellType) 10 { 11 case CellType.Numeric: 12 // Date comes here 13 if (DateUtil.IsCellDateFormatted(cell)) 14 { 15 value = cell.DateCellValue; 16 } 17 else 18 { 19 // Numeric type 20 value = cell.NumericCellValue; 21 } 22 break; 23 case CellType.Boolean: 24 // Boolean type 25 value = cell.BooleanCellValue; 26 break; 27 case CellType.Formula: 28 value = cell.CellFormula; 29 break; 30 default: 31 // String type 32 value = cell.StringCellValue; 33 break; 34 } 35 } 36 } 37 catch (Exception) 38 { 39 value = ""; 40 } 41 return value; 42 }
特别注意的是CellType
中没有Date,而日期类型的数据类型是Numeric
,其实日期的数据在Excel中也是以数字的形式存储。可以使用DateUtil.IsCellDateFormatted
方法来判断是否是日期类型。
有了GetCellValue
方法,写数据到Excel中的时候就要有SetCellValue
方法,缺的类型可以自己补。
1 //根据数据类型设置不同类型的cell 2 public static void SetCellValue(ICell cell, object obj) 3 { 4 if (obj.GetType() == typeof(int)) 5 { 6 cell.SetCellValue((int)obj); 7 } 8 else if (obj.GetType() == typeof(double)) 9 { 10 cell.SetCellValue((double)obj); 11 } 12 else if (obj.GetType() == typeof(IRichTextString)) 13 { 14 cell.SetCellValue((IRichTextString)obj); 15 } 16 else if (obj.GetType() == typeof(string)) 17 { 18 cell.SetCellValue(obj.ToString()); 19 } 20 else if (obj.GetType() == typeof(DateTime)) 21 { 22 cell.SetCellValue((DateTime)obj); 23 } 24 else if (obj.GetType() == typeof(bool)) 25 { 26 cell.SetCellValue((bool)obj); 27 } 28 else 29 { 30 cell.SetCellValue(obj.ToString()); 31 } 32 }
cell.SetCellValue()
方法只有四种重载方法,参数分别是string
, bool
, DateTime
, double
, IRichTextString
设置公式使用cell.SetCellFormula(string formula)
写Excel
以下是简单的例子,更多信息可以参见官网提供的Examples。
1 public void WriteToExcel(string filePath) 2 { 3 //创建工作薄 4 IWorkbook wb; 5 string extension = System.IO.Path.GetExtension(filePath); 6 //根据指定的文件格式创建对应的类 7 if (extension.Equals(".xls")) 8 { 9 wb = new HSSFWorkbook(); 10 } 11 else 12 { 13 wb = new XSSFWorkbook(); 14 } 15 ICellStyle style1 = wb.CreateCellStyle();//样式 16 style1.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Left;//文字水平对齐方式 17 style1.VerticalAlignment = NPOI.SS.UserModel.VerticalAlignment.Center;//文字垂直对齐方式 18 //设置边框 19 style1.BorderBottom = NPOI.SS.UserModel.BorderStyle.Thin; 20 style1.BorderLeft = NPOI.SS.UserModel.BorderStyle.Thin; 21 style1.BorderRight = NPOI.SS.UserModel.BorderStyle.Thin; 22 style1.BorderTop = NPOI.SS.UserModel.BorderStyle.Thin; 23 style1.WrapText = true;//自动换行 24 ICellStyle style2 = wb.CreateCellStyle();//样式 25 IFont font1 = wb.CreateFont();//字体 26 font1.FontName = "楷体"; 27 font1.Color = HSSFColor.Red.Index;//字体颜色 28 font1.Boldweight = (short)FontBoldWeight.Normal;//字体加粗样式 29 style2.SetFont(font1);//样式里的字体设置具体的字体样式 30 //设置背景色 31 style2.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; 32 style2.FillPattern = FillPattern.SolidForeground; 33 style2.FillBackgroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index; 34 style2.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Left;//文字水平对齐方式 35 style2.VerticalAlignment = NPOI.SS.UserModel.VerticalAlignment.Center;//文字垂直对齐方式 36 ICellStyle dateStyle = wb.CreateCellStyle();//样式 37 dateStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Left;//文字水平对齐方式 38 dateStyle.VerticalAlignment = NPOI.SS.UserModel.VerticalAlignment.Center;//文字垂直对齐方式 39 //设置数据显示格式 40 IDataFormat dataFormatCustom = wb.CreateDataFormat(); 41 dateStyle.DataFormat = dataFormatCustom.GetFormat("yyyy-MM-dd HH:mm:ss"); 42 //创建一个表单 43 ISheet sheet = wb.CreateSheet("Sheet0"); 44 //设置列宽 45 int[] columnWidth = { 10, 10, 20, 10 }; 46 for (int i = 0; i < columnWidth.Length; i++) 47 { 48 //设置列宽度,256*字符数,因为单位是1/256个字符 49 sheet.SetColumnWidth(i, 256 * columnWidth[i]); 50 } 51 //测试数据 52 int rowCount = 3, columnCount = 4; 53 object[,] data = { 54 {"列0", "列1", "列2", "列3"}, 55 {"", 400, 5.2, 6.01}, 56 {"", true, "2014-07-02", DateTime.Now} 57 //日期可以直接传字符串,NPOI会自动识别 58 //如果是DateTime类型,则要设置CellStyle.DataFormat,否则会显示为数字 59 }; 60 IRow row; 61 ICell cell; 62 63 for (int i = 0; i < rowCount; i++) 64 { 65 row = sheet.CreateRow(i);//创建第i行 66 for (int j = 0; j < columnCount; j++) 67 { 68 cell = row.CreateCell(j);//创建第j列 69 cell.CellStyle = j % 2 == 0 ? style1 : style2; 70 //根据数据类型设置不同类型的cell 71 object obj = data[i, j]; 72 SetCellValue(cell, data[i, j]); 73 //如果是日期,则设置日期显示的格式 74 if (obj.GetType() == typeof(DateTime)) 75 { 76 cell.CellStyle = dateStyle; 77 } 78 //如果要根据内容自动调整列宽,需要先setCellValue再调用 79 //sheet.AutoSizeColumn(j); 80 } 81 } 82 //合并单元格,如果要合并的单元格中都有数据,只会保留左上角的 83 //CellRangeAddress(0, 2, 0, 0),合并0-2行,0-0列的单元格 84 CellRangeAddress region = new CellRangeAddress(0, 2, 0, 0); 85 sheet.AddMergedRegion(region); 86 try 87 { 88 FileStream fs = File.OpenWrite(filePath); 89 wb.Write(fs);//向打开的这个Excel文件中写入表单并保存。 90 fs.Close(); 91 } 92 catch (Exception e) 93 { 94 Debug.WriteLine(e.Message); 95 } 96 }
如果想要设置单元格为只读或可写,可以参考这里,方法如下:
1 ICellStyle unlocked = wb.CreateCellStyle(); 2 unlocked.IsLocked = false;//设置该单元格为非锁定 3 cell.SetCellValue("未被锁定"); 4 cell.CellStyle = unlocked; 5 ... 6 //保护表单,password为解锁密码 7 //cell.CellStyle.IsLocked = true;的单元格将为只读 8 sheet.ProtectSheet("password");
cell.CellStyle.IsLocked
默认就是true,因此sheet.ProtectSheet("password")
一定要执行,才能实现锁定单元格,对于不想锁定的单元格,就一定要设置cell
的CellStyle
中的IsLocked = false