废不多,直入正题。
所需环境:安装了Windows操作系统和Office软件的电脑一台。
开发语言:C#
开发需求:1.利用C#脚本读取Excel .xlsx文件
2.将程序中的数据存储到.csv文件当中
可以看到,我们的项目完全脱离了IDE,这也方便大家能够进行快速的开发。毕竟有的时候手边的计算机不一定安装了vs这样的集成开发环境。那么我们首先就要自己去找到C#的编译器:
csc.exe 一般位于:C:\Windows\Microsoft.NET\Framework 选择一个版本 文件夹下。可以将这个目录放到环境变量path中去,以便于在命令窗口快速使用:
如上图所示即证明csc环境已经配置好。编译器已经有了,下面我们还需要一个处理Excel所需的动态库,这个库可以在Office软件的安装目录中找到,名字叫做:
Microsoft.Office.Interop.Excel.dll 把它复制出来一份 。
新建一个文件夹,把刚才找到的dll放到这个文件夹下,然后在这个文件夹下创建一个.cs C#脚本,如 excelexecutor.cs
1.用编辑器打开,然后添加指令集:
using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; using System.IO; using Excel = Microsoft.Office.Interop.Excel; using System.Data.OleDb; using System.Runtime.InteropServices;
2.创建命名空间和类,并在类中加载动态库:
namespace rockderia { public class Program { [DllImport("kernel32.dll")] private static extern IntPtr _lopen(string lpPathName, int iReadWrite); [DllImport("kernel32.dll")] private static extern bool CloseHandle(IntPtr hObject); private const int OF_READWRITE = 2; private const int OF_SHARE_DENY_NONE = 0x40; private static readonly IntPtr HFILE_ERROR = new IntPtr(-1); } }
3.编写第一个静态函数:检测文件是否已经被打开。
功能说明:如果其他的程序打开了某个Excel文件,原则上这个Excel文件将不会被更改。所以这里写这个函数专门用来检测。如果文件已经被打开,那么将不会对它执行操作。
public static int FileIsOpen(string fileFullName) { if(!File.Exists(fileFullName)) { return -1; } IntPtr handle = _lopen(fileFullName, OF_READWRITE | OF_SHARE_DENY_NONE); if (handle == HFILE_ERROR) { return 1; } CloseHandle(handle); return 0; }
4.编写第二个静态函数:加载Excel文件:
[1]Excel文件中可能有多个表格,就是下方的sheet1 sheet2本个例子先只操作sheet1
[2]表格的第一行一般默认为标题 在strCon字符串中有一项属性HDR设置成NO的话确实可以将标题当数据读出,但是可能会出错!所以不建议那么做,而是单独的将标题读取在一个返回值中。其余的列装到一个返回值中。额外的返回值可以用out 参数来处理。
[3]函数开始前调用上边的方法检测文件状态。
[4]filePath是要读取的文件的路径
函数声明如下:
public static List<string[]> loading(string filePath, out List<string> retHead)
具体代码
public static List<string[]> loading(string filePath, out List<string> retHead) { if(FileIsOpen(filePath) == 1) { Console.WriteLine("文件在其他位置已经被打开!"); retHead = new List<string>(); return null; } else { string strCon = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filePath + ";Extended Properties='Excel 12.0;HDR=YES;IMEX=2'"; OleDbConnection myConn = new OleDbConnection(strCon); myConn.Open(); //返回Excel的架构,包括各个sheet表的名称,类型,创建时间和修改时间等 DataTable dtSheetName = myConn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "Table" }); //包含excel中表名的字符串数组 //这块处理的是表的集合和名字,也就是excel下方的角标处的列表(例如sheet1 sheet2)。将所有的表的名字存在strTableNames当中了。 string[] strTableNames = new string[dtSheetName.Rows.Count]; for (int k = 0; k < dtSheetName.Rows.Count; k++) { strTableNames[k] = dtSheetName.Rows[k]["TABLE_NAME"].ToString(); } string strCom = "select * from [" + strTableNames[0] + "]"; ; //这里开始,针对表0进行操作(即第一个表 如sheet1)。 OleDbDataAdapter myDataAdapter = new OleDbDataAdapter(strCom, myConn); DataSet myDataSet = new DataSet(); myDataAdapter.Fill(myDataSet, strTableNames[0]); List<string[]> retList = new List<string[]>(); retHead = new List<string>();
//标题数据需要像这样额外获取 存储在List<string>中 for(int i = 0; i < myDataSet.Tables[0].Columns.Count; i++) { retHead.Add(myDataSet.Tables[0].Columns[i].ToString()); }
//获取非标题的各行数据 存储在List<string[]>中 DataTable dt = myDataSet.Tables[0]; foreach (DataRow r in dt.Rows) { int colCount = r.ItemArray.Count(); string[] items = new string[colCount]; for (int i = 0; i < colCount; i++) { items[i] = Convert.ToString(r.ItemArray[i]); } retList.Add(items); } myConn.Close(); return retList; } }
5.编写静态方法,生成一个.csv文件
[1]这个静态方法写入.csv文件的所有数据,需要存储在DataTable结构中,可以用下面的例子简单构造:
DataTable dt1 = new DataTable(); dt1.Columns.Add("姓名"); dt1.Columns.Add("年龄"); dt1.Columns.Add("性别"); dt1.Columns.Add("??"); for(int i = 0; i < 10; i++) { DataRow dw = dt1.NewRow(); dw["姓名"] = "阿卡林"; dw["年龄"] = "阿卡林"; dw["性别"] = "阿卡林"; dw["??"] = "阿卡林"; dt1.Rows.Add(dw); }
其中Columns就是标题行中各个列的标题。
写文件函数代码如下:
public static bool DataToExcel(DataTable m_DataTable, string file_new_path) { string FileName = file_new_path; if (FileName == "") { Console.WriteLine("文件路径错误!"); return false; } else { if (File.Exists(FileName)) File.Delete(FileName); FileStream objFileStream; StreamWriter objStreamWriter; string strLine = ""; objFileStream = new FileStream(FileName, FileMode.OpenOrCreate, FileAccess.Write); objStreamWriter = new StreamWriter(objFileStream, System.Text.Encoding.Unicode); for (int i = 0; i < m_DataTable.Columns.Count; i++) { strLine = strLine + m_DataTable.Columns[i].Caption.ToString() + Convert.ToChar(9); } objStreamWriter.WriteLine(strLine); strLine = ""; for (int i = 0; i < m_DataTable.Rows.Count; i++) { for (int j = 0; j < m_DataTable.Columns.Count; j++) { if (m_DataTable.Rows[i].ItemArray[j] == null) strLine = strLine + " " + Convert.ToChar(9); else { string rowstr = ""; rowstr = m_DataTable.Rows[i].ItemArray[j].ToString(); if (rowstr.IndexOf("\r\n") > 0) rowstr = rowstr.Replace("\r\n", " "); if (rowstr.IndexOf("\t") > 0) rowstr = rowstr.Replace("\t", " "); strLine = strLine + rowstr + Convert.ToChar(9); } } objStreamWriter.WriteLine(strLine); strLine = ""; } objStreamWriter.Close(); objFileStream.Close(); return true; }
这样,读写的功能就都有了,可以编写一个Main函数来测试这两个方法亦或是执行一系列的逻辑,这里就不多提了,最后就是编译了。
一般来说编译一个.cs文件 这个.cs要有Main函数就可以了,但是本例中使用到了动态库,所以编译的命令应该是
csc /r:Microsoft.Office.Interop.Excel.dll .\excelexecutor.cs
编译完成后,excelexecutor.exe就可以直接执行了~