好幾天沒有寫博客了,突然間有種寫博客的沖動,雖然我的技術能力還是在努力的進步,但是和那些大牛比起來,我確實是小菜一枚,不過這並沒有打擊我的積極性,反而讓我有種沖動,去超越他們。哈哈
我記得在幾天前我說過一個Lucene.net,是從java中借鑒過來的,現在我來說一下Npoi吧,當然這個也是從java借鑒過來的,不過這個語法沒有Lucene.net那么惡心,Lucene.net是完全的java版,但是npoi.net最起碼有點和.net聯系在了一起。好東西都是java的,.net大牛們,你們在干嘛,開發一些優秀的東西吧,別總讓java鄙視我們。
好了,說說Npoi.net吧,Npoi是對Excel(或者叫office比較合適)進行操作的一個類庫框架,我們可以不再使用office提供的那個惡心的接口(速度慢)來操作了,我們對於office的操作解放了。現在最新版的應該是1.2.5.如果你安裝了NuGet的話,可以很容易的獲取。
因為Excel 2003極其以前的版本采用的底層代碼和Excel 2007 及其以后的不同,所以對於這個分界點是需要我們注意的。為什么會出現這個分界點,大家想一下,現在微軟的所有東西都在.net 架構之下,以前的Excel版本和這個架構肯定是不同的。
我們重點來說excel。畢竟這是在我們的工作中用的最多的一個,Excel包括文件(file,在Npoi.net中稱為workBook 工作簿)、工作表(sheet)以及里面的單元格,按照從大到小的分類就是文件中包含若干個sheet,每個sheet中包含很多單元格。這是Excel的分類。
為了操作Excel,npoi.net當然也需要對應的類來對應不同的分類,其中
HSSFWorkbook對應的就是Excel文件 工作簿,
HSSFSheet對應的就是Excel中sheet 工作表,
HSSFCell對應的就是Excel的單元格,
HSSFRow對應的就是Excel的行。
有了這幾個不同的類,我們就可以創建Excel文件,創建sheet,給sheet重命名或刪除sheet,創建行,添加單元格,為單元格設置格式,我個人感覺其他的都很簡單,唯獨這個給單元格設置格式的做法很令人討厭,沒辦法用的別人的,只能有苦自己承受了。
下面我就舉一個很簡單的例子來說明一下npoi.net的整體使用,如果有任何說錯的地方,還請你指出來,小菜謝過了。
我個人認為會遇到的問題我都在注釋中寫了,
1 public class NpoiNet 2 { 3 public void CreateWorkBook() 4 { 5 string filePath = HttpContext.Current.Server.MapPath("~/npoi.xls"); 6 //表示一個文件流
7 FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite); 8 HSSFWorkbook workBook = new HSSFWorkbook();//相當於創建了一個內存中的Excel 只是還沒有寫到硬盤上
9 try
10 { 11 /*按照約定在每個Excel文件中至少要包含一個sheet,如果沒有任何sheet,打開Excel文件的時候會報錯。 12 在以前的npoi版本中,如果不是自己手動的創建一個sheet,npoi框架是不會自動創建一個的,但是在最新版的1.2.5中, 13 我發現即使你忘記了創建sheet的操作,框架會自動創建一個sheet,當然如果你手動創建了一個sheet,則框架就不會 14 自動的創建一個sheet,這就像構造函數,如果沒有寫任何一個,.Net會默認存在一個,但是你創建了一個構造函數,則 15 * 框架就不會自動創建了 16 * */
17
18 workBook.CreateSheet("guozhiqi");//sheet的名稱為guozhiqi
19 workBook.CreateSheet();//sheet的名稱為sheet1 從1開始命名
20 workBook.CreateSheet("yuanjinzhou"); 21 workBook.CreateSheet();//這個sheet的名稱應該為什么?答案是sheet3 22 //創建了sheet 下面我們來創建單元格
23 HSSFSheet sheetGuozhiqi = (HSSFSheet)workBook.GetSheet("guozhiqi");//獲取到sheet名稱為guozhiqi的那個工作簿 24 //創建了工作簿 添加單元格之前首先要添加行 25 //在這里請注意 行和列的下標都是從0開始的 而不是Excel文件中的1
26 for (int i = 0; i <= 10; i++) 27 { 28 sheetGuozhiqi.CreateRow(i);//創建了11行
29 } 30
31 //創建了行之后 就要在每個行中創建單元格 32 //如果沒有在之前創建行 33 // HSSFRow newRow = (HSSFRow)sheetGuozhiqi.GetRow(12);這是錯誤的 程序會報異常 這就告訴我們 如果行還沒有創建 34 //就不可以獲取
35 HSSFRow newRow = (HSSFRow)sheetGuozhiqi.GetRow(0); 36
37 HSSFCell[] cells = new HSSFCell[11]; 38 //在第0行的位置創建單元格
39 for (int i = 0; i <= 10; i++) 40 { 41 cells[i] = (HSSFCell)newRow.CreateCell(i);//為第0行創建了10個單元格
42 } 43 //這里取得單元格的異常出現位置相同 就是如果沒有創建就不要試圖獲取
44 HSSFCell newCell = (HSSFCell)newRow.GetCell(0);//取得第0行第0個單元格 45
46 //獲取到了單元格 賦值 為了方便說明 我多獲取幾個單元格的值 47 //Excel單元格有很多類型 例如字符串 數字 bool等類型,
48 cells[0].SetCellValue(false);//賦值為bool型
49 cells[1].SetCellValue(DateTime.Now);//賦值為日期型
50 cells[2].SetCellValue(3.1415926);//賦值為double類型
51 cells[3].SetCellValue("guozhiqi");//賦值為字符串guozhiqi 52
53 //為單元格賦值以后 我們就要開始取值了
54 bool cell0 = cells[0].BooleanCellValue; 55 // string cell00 = cells[0].StringCellValue; 錯誤 這句話的意思就是說存儲的時候是什么類型 就必須用對應的 56 //取值方法取值
57 DateTime dateTime = cells[1].DateCellValue; 58 double cell2 = cells[2].NumericCellValue; 59 string cell3 = cells[3].StringCellValue; 60
61 //可以正常創建單元格 從單元格取值是遠遠不夠的 還有就是客戶可能會要求合並單元格 為單元格設置樣式 62 //合並單元格的操作不必要創建所有的行或列,只需要制定范圍即可
63 sheetGuozhiqi.AddMergedRegion(new NPOI.SS.Util.Region(1,1,20,20));//合並單元格
64
65 HSSFCellStyle cellStyle = (HSSFCellStyle)workBook.CreateCellStyle(); 66 cellStyle.Alignment = HorizontalAlignment.CENTER;//居中顯示
67 cellStyle.FillBackgroundColor = 244; 68 cellStyle.FillPattern = FillPatternType.BRICKS;//填充模式
69 cellStyle.IsHidden = false;//單元格是否隱藏
70 cellStyle.IsLocked = false;//單元格是否鎖定
71 cellStyle.VerticalAlignment = VerticalAlignment.CENTER;//垂直居中 72
73 //設置單元格字體
74 HSSFFont font =(HSSFFont) workBook.CreateFont(); 75 font.Color = 200; 76 font.FontHeight = 18;//設置字體大小
77 font.FontName = "黑體";//設置字體為黑體
78 font.IsItalic = false;//是否是斜體
79 font.IsStrikeout = true;//是否有中間線
80 font.Underline = (byte)FontUnderlineType.DOUBLE;//設置下划線
81
82 cellStyle.SetFont(font); 83 //將設置好的樣式應用到對應的單元格上 否則是沒有效果的
84 cells[0].CellStyle = cellStyle; 85
86 if (!workBook.IsWriteProtected) 87 { 88 workBook.Write(fileStream); 89 } 90 } 91 catch (Exception ex) 92 { 93 HttpContext.Current.Response.Write(ex.Message+ex.Source+ex.StackTrace); 94 } 95 finally
96 { 97 if (fileStream != null) 98 { 99 fileStream.Close(); 100 } 101 } 102 } 103 }
總結,既然這篇博客是Npoi.net的總結,那么我不可能就是舉了一個例子就算了事,畢竟博客園中園友們舉得例子已經夠多了,下面我就把在使用npoi.net會遇到的一些小問題總結一下。
- 在實例化了一個WorkBook之后,最好添加一個sheet,雖然在最新版的Npoi.net中自動添加了,但是遇到遷移到原來版本就會出現問題。所以我建議還是最少添加一個sheet
- 在從單元格取值時要注意單元格的類型,一定要用對應的類型的方法來取單元格中的對應類型的值,如果不確定,那只能是強制轉換成為string類型,畢竟string類型是excel中其他類型都可以轉換過來的
- 在獲取sheet中的某一行或者某一行的某一個單元格的時候,還要注意就是一定要確保創建了該行,並且取單元格還要確保創建了單元格,否則會報Null reference not to object 這個我們經常會看到的異常信息。在外層一定要加上try捕獲異常
- 合並單元格是sheet的工作,因此需要獲取對應的sheet,然后調用其AddMergedRegion方法合並單元格,在合並單元格的時候,我們不需要確保該單元格已經存在或創建。
- 在為單元格設置樣式的過程中,我們會發現所有和樣式相關的類的創建都是通過workBook.Create(Font)..這種方式來執行的,我們不可以直接new一個類的實例。
- 如果前面的工作都已經做好,需要把內存中的excel表寫到硬盤上時,需要調用workBook.write()方法,傳入一個文件流進行創建。在這里有可能會出現一個問題,就是你要創建的文件你已經打開了,這時程序就會出現異常,因此我們在調試程序的時候一定要記得打開了excel文件以后要關閉
- 最后需要注意的就是文件流,在我們把excel寫到硬盤上以后,要顯式的調用其close()方法關閉文件流。因為如果不關閉文件流的話,以后就會出現無法重新創建該文件的錯誤,並且會提示 某文件正由另一進程使用,因此該進程無法訪問此文件。
好了,我能想到的基本上都寫出來了,感覺好多了,一會再分析一下c#類和結構的區別。相信很多園友都知道,但是不一定會很明確,那么我就來幫大家回憶一下曾經的學習。夜深人靜,確實是寫作的好時候。
