2013年5月24日 星期五

Excel程式:使用Oledb(C#)

  首先須引用System.Data.OleDb,接著是定義變數部分,如下:

   1: OleDbConnection OleDBConn_Excel = new OleDbConnection();
   2: OleDbCommand OleDBCmd_Excel = new OleDbCommand();
   3: OleDBCmd_Excel.CommandType = CommandType.Text;
   4: OleDBCmd_Excel.Connection = OleDBConn_Excel;
  第一部份是要連線Connect的部份,如下:



   1: private bool ConnectExcelDB(string ExcelFilePath, bool isFirstRowHeader, out string RetDesc)
   2: {
   3:     string header = isFirstRowHeader ? "Yes" : "No";
   4:     //Microsoft.ACE.OLEDB.12.0                
   5:     OleDBConn_Excel.ConnectionString = @"Provider=Microsoft.ACE.OLEDB.12.0;DataSource=" +        ExcelFilePath +   ";Extended Properties=\"Excel 12.0;HDR=" + header + "\"";
   6:     try
   7:     {
   8:         if (OleDBConn_Excel.State == ConnectionState.Open)
   9:             OleDBConn_Excel.Close();
  10:         OleDBConn_Excel.Open();
  11:         RetDesc = string.Empty;
  12:         return true;
  13:     }
  14:     catch (OleDbException ex)
  15:     {
  16:         RetDesc = ex.Message;
  17:         return false;
  18:     }
  19: }
先解釋一下上面的傳入變數,ExcelFilePath,顧名思義就是Excel檔案所在路徑;isFirstRowHeader變數則是用於設定Excel檔案Sheet中的資料列第一列是否為欄位名稱,若設定為true,表示第一列當作欄位名稱,反則無;RetDesc則是回傳字串,若出現Error則會帶ErrorMessage回傳,反則屬空字串,之後的Function皆會使用到這個變數,因此爾後遇到不再多加解釋。利用Microsoft.ACE.OLEDB.12.0將OledbConn_Excel打開。 接著是先示範一下Connect成功後做一個DataReader的部份,如下: 


   1: /// <summary>
   2: /// 取得ExcelDB DataReader物件
   3: /// </summary>
   4: /// <param name="SqlStr">Sql語句</param>
   5: /// <param name="RetDesc">錯誤回傳訊息</param>
   6: /// <returns>回傳OleDBDataReader物件</returns>
   7: public OleDbDataReader GetExcelDBDataReader(string SqlStr, out string RetDesc)
   8: {
   9:     if (mDBType != DBType.Excel)
  10:     {
  11:         RetDesc = "DB Type is not Excel"; 
  12:         return null;
  13:     }
  14:     //假如未建立連線則先建立
  15:     RetDesc = string.Empty;
  16:     if (OleDBConn_Excel == null)
  17:         if (!ConnectExcelDB(mExcelFilePath,misFirstRowHeader, out RetDesc))
  18:             return null;
  19:     try 
  20:     {
  21:         //假如連線中斷,則先Open之
  22:         if (OleDBConn_Excel.State == ConnectionState.Closed)
  23:             OleDBConn_Excel.Open();
  24:         OleDBCmd_Excel.CommandText = SqlStr;
  25:         return OleDBCmd_Excel.ExecuteReader();
  26:         }
  27:     catch (Exception ex)
  28:     {
  29:         RetDesc = ex.Message;
  30:         return null;
  31:     }
  32: }

是否為null,若是則使用上面Func:ConnectExcelDB連線,最後面將傳入的Sqlstr指定至OleDBCmd_Excel之CommandText,最後回傳ExcuteReader之結果。最後我們做事情要有始有終,連線打開了記得要關閉它,DisconnectFunction如下:




   1: private void CloseExcelDB()
   2: {
   3:     try
   4:     {
   5:         if (OleDBConn_Excel.State == ConnectionState.Open)
   6:             OleDBConn_Excel.Close();
   7:         
   8:         OleDBConn_Excel.Dispose();  //釋放資源 
   9:         }
  10:     catch (Exception ex)
  11:     { 
  12:         throw new Exception(ex.Message);
  13:     }
  14: }


我們來做一下實例吧,使用上面這些Function,我使用的範例如下:

image

接著如下圖拉出一個Button以及Label,Button用意是我們把讀取Excel的程式撰寫在click事件中,接著將結果秀在Label上面。

image

接著是著手寫Button_Click事件,如下:

   1: private void btnRead_Click(object sender, EventArgs e)
   2: {
   3:     string path = Application.StartupPath + @"\TestExcel.xlsx";
   4:     string Errmsg = "";
   5:     if (ConnectExcelDB(path,false,out Errmsg))
   6:     {
   7:         //Read:OleDbDataReader
   8:         string sqlstr = "SELECT * FROM [Sheet1$B2:B2] ";
   9:         OleDbDataReader excelreader = GetExcelDBDataReader(sqlstr, out Errmsg);
  10:         if (Errmsg == "")
  11:         {
  12:             if (excelreader.Read())
  13:             {
  14:                 label1.Text = excelreader[0].ToString();
  15:             }
  16:             excelreader.Close();
  17:         }
  18:         else
  19:             MessageBox.Show(Errmsg);
  20:         CloseExcelDB();
  21:     }
  22:     else
  23:         MessageBox.Show(Errmsg);
  24: } 



上面的語法就是我要撈出Sheet1當中的B2位置的值,執行結果如下:

image

2013年5月23日 星期四

搜尋目錄下所有檔案with pattern

  我的需求是分析一個路徑,此路徑會不定時更新資料,資料夾亦會增加。我的程式是要去掃那一個路徑底下的所有符合的檔案撈出並分析。說了自動化就是只能設一個路徑,需要程式去掃描路徑下所有符合檔案,這時需要以下程式去幫助完成。
  我從此網誌:Getfiles參考而來,但我是使用網誌下留言者的方法,用Recursive簡潔有力。以下是先呈現此Function。

static IEnumerable GetFiles(string dir, string pattern, bool isRecursive)
{
    var dinfo = new DirectoryInfo(dir);
    foreach (FileInfo f in dinfo.GetFiles(pattern))
        yield return f;
    if (isRecursive)
    {
        foreach (DirectoryInfo subdir in dinfo.GetDirectories())
            foreach (FileInfo fi in GetFiles(subdir.FullName, pattern, true))
                yield return fi;
    }
}
  接下來是我的應用,以下以搜尋pdf檔案為例。

private void btnGetFileSet_Click(object sender, EventArgs e)
        {
            if (txtFilePath.Text == "")
            {
                MessageBox.Show("路徑不可為空");
                return;
            }
            DataTable dt = new DataTable();
            DataColumn dc5 = new DataColumn("列", typeof(string));
            dt.Columns.Add(dc5);
            DataColumn dc1 = new DataColumn("檔名", typeof(string));
            dt.Columns.Add(dc1);
            DataColumn dc2 = new DataColumn("建立日期", typeof(string));
            dt.Columns.Add(dc2);
            DataColumn dc3 = new DataColumn("最後存取時間", typeof(string));
            dt.Columns.Add(dc3);
            DataColumn dc4 = new DataColumn("完整路徑", typeof(string));
            dt.Columns.Add(dc4);                      

            IEnumerable fileset = GetFiles(txtFilePath.Text, "*.pdf", true);
            int i = 1;
            foreach (FileInfo fileinfo in fileset)
            {
                DataRow dr = dt.NewRow();
                dr["列"] = i;
                dr["檔名"] = fileinfo.Name;
                dr["建立日期"] = fileinfo.CreationTime.ToString("yyyy/MM/dd HH:mm:ss");
                dr["最後存取時間"] = fileinfo.LastAccessTime.ToString("yyyy/MM/dd HH:mm:ss");
                dr["完整路徑"] = fileinfo.FullName;
                dt.Rows.Add(dr);
                i++;
            }

            bindingSource1.DataSource = dt;
            dGV.AutoGenerateColumns = true;
            dGV.DataSource = bindingSource1;
        }
  我寫在一個button_click事件中,我搜尋的條件是"*.pdf",意思就是結尾為.pdf的檔案,然後將符合搜尋條件的檔案的檔名,建立日期,最後存取時間,完整路徑等資訊列在DataGridView當中。其中用Function的結果:IEnumerable<fileinfo>再用foreach的方式將每一個fileinfo的資訊列加入至datatable完成,Recursive好壓。
  更正:以上程式碼均出現IEnumerable 改為 IEnumerable<fileinfo>,我用的顯示程式碼模板會將<fileinfo>當作html碼沒有秀在上面,請善用。