本文參考自:https://blog.csdn.net/mss359681091/article/details/52078147 本文所有需要用到的文件下載包含項目: Lucene配置文件下載 本文項目下載Zip 用vs2015創建Windows窗體應用程式,創建好項目時記得將其屬性改為“控制台應 ...
本文參考自:https://blog.csdn.net/mss359681091/article/details/52078147
本文所有需要用到的文件下載包含項目:
用vs2015創建Windows窗體應用程式,創建好項目時記得將其屬性改為“控制台應用程式”,當然也可以是預設的,只是這樣方便些。如下圖
1.一元分詞法
除此外,還需要引用’Lucene.Net.dll‘

1 /// <summary> 2 /// 一元分詞法 3 /// </summary> 4 /// <param name="sender"></param> 5 /// <param name="e"></param> 6 private void button1_Click(object sender, EventArgs e) 7 { 8 Analyzer analyzer = new StandardAnalyzer(); // 標準分詞 → 一元分詞 9 TokenStream tokenStream = analyzer.TokenStream("", new StringReader("喝奶只喝純牛奶,這是不可能的——黑夜中的螢火蟲")); 10 Token token = null; 11 while ((token = tokenStream.Next()) != null) // 只要還有詞,就不返回null 12 { 13 string word = token.TermText(); // token.TermText() 取得當前分詞 14 Console.Write(word + " | "); 15 } 16 }一元分詞法
2.二元分詞法
在剛纔的基礎上,再引用文件夾“Analyzers”中的兩個.cs文件,如下圖

/// <summary> /// 二元分詞 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click(object sender, EventArgs e) { Analyzer analyzer = new CJKAnalyzer(); // 標準分詞 → 一元分詞 TokenStream tokenStream = analyzer.TokenStream("", new StringReader("喝奶只喝純牛奶,這是不可能的——黑夜中的螢火蟲")); Token token = null; while ((token = tokenStream.Next()) != null) // 只要還有詞,就不返回null { string word = token.TermText(); // token.TermText() 取得當前分詞 Console.Write(word + " | "); } }二元分詞法
3.盤古分詞法
再引用以下兩個配置文件

/// <summary> /// 盤古分詞法 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button3_Click(object sender, EventArgs e) { Analyzer analyzer = new PanGuAnalyzer(); // 盤古分詞 TokenStream tokenStream = analyzer.TokenStream("", new StringReader("喝奶只喝純牛奶,這是不可能的——黑夜中的螢火蟲")); Token token = null; while ((token = tokenStream.Next()) != null) // 只要還有詞,就不返回null { string word = token.TermText(); // token.TermText() 取得當前分詞 Console.Write(word + " | "); } }盤古分詞法
如果不去更改,盤古詞包中並不包含所需辭彙,以下是運行效果圖
而用‘DictManage.exe‘來打開項目中的Dict.dct文件,添加辭彙,並加以保存。
下圖是修改後的運行效果:
4.簡單搜索
創建web窗體SearchWords.aspx,如下圖

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="SearchWords.aspx.cs" Inherits="PanGu_Search.Views.SearchWords" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>最簡單的搜索引擎</title> <script> $(document).keydown(function (event) { if (event.keyCode == 13) { $("#btnGetSearchResult").click(); } }); </script> </head> <body> <form id="mainForm" runat="server"> <div align="center"> <asp:Button ID="btnCreateIndex" runat="server" Text="Create Index" OnClick="btnCreateIndex_Click" /> <asp:Label ID="lblIndexStatus" runat="server" Visible="false" /> <hr /> <asp:TextBox ID="txtKeyWords" runat="server" Text="" Width="250"></asp:TextBox> <asp:Button ID="btnGetSearchResult" runat="server" Text="Search" OnClick="btnGetSearchResult_Click" /> <hr /> </div> <div> <ul> <asp:Repeater ID="rptSearchResult" runat="server"> <ItemTemplate> <li>Id:<%#Eval("Id") %><br /><%#Eval("Msg") %></li> </ItemTemplate> </asp:Repeater> </ul> </div> </form> </body> </html>前端aspx設計

/// <summary> /// 創建索引方法 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void btnCreateIndex_Click(object sender, EventArgs e) { string indexPath = Context.Server.MapPath("~/Index"); // 索引文檔保存位置 FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NativeFSLockFactory()); bool isUpdate = IndexReader.IndexExists(directory); //判斷索引庫是否存在 if (isUpdate) { // 如果索引目錄被鎖定(比如索引過程中程式異常退出),則首先解鎖 // Lucene.Net在寫索引庫之前會自動加鎖,在close的時候會自動解鎖 // 不能多線程執行,只能處理意外被永遠鎖定的情況 if (IndexWriter.IsLocked(directory)) { IndexWriter.Unlock(directory); //unlock:強制解鎖,待優化 } } // 創建向索引庫寫操作對象 IndexWriter(索引目錄,指定使用盤古分詞進行切詞,最大寫入長度限制) // 補充:使用IndexWriter打開directory時會自動對索引庫文件上鎖 IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), !isUpdate, IndexWriter.MaxFieldLength.UNLIMITED); for (int i = 1; i < 3; i++) { string txt = File.ReadAllText(Context.Server.MapPath("~/Upload/Articles/") + i + ".txt"); // 一條Document相當於一條記錄 Document document = new Document(); // 每個Document可以有自己的屬性(欄位),所有欄位名都是自定義的,值都是string類型 // Field.Store.YES不僅要對文章進行分詞記錄,也要保存原文,就不用去資料庫里查一次了 document.Add(new Field("id", i.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); // 需要進行全文檢索的欄位加 Field.Index. ANALYZED // Field.Index.ANALYZED:指定文章內容按照分詞後結果保存,否則無法實現後續的模糊查詢 // WITH_POSITIONS_OFFSETS:指示不僅保存分割後的詞,還保存詞之間的距離 document.Add(new Field("msg", txt, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS)); // 防止重覆索引,如果不存在則刪除0條 writer.DeleteDocuments(new Term("id", i.ToString()));// 防止已存在的數據 => delete from t where id=i // 把文檔寫入索引庫 writer.AddDocument(document); Console.WriteLine("索引{0}創建完畢", i.ToString()); } writer.Close(); // Close後自動對索引庫文件解鎖 directory.Close(); // 不要忘了Close,否則索引結果搜不到 lblIndexStatus.Text = "索引文件創建成功!"; lblIndexStatus.Visible = true; btnCreateIndex.Enabled = false; }創建索引方法

/// <summary> /// 搜索方法 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void btnGetSearchResult_Click(object sender, EventArgs e) { string keyword = txtKeyWords.Text; string indexPath = Context.Server.MapPath("~/Index"); // 索引文檔保存位置 FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NoLockFactory()); IndexReader reader = IndexReader.Open(directory, true); IndexSearcher searcher = new IndexSearcher(reader); // 查詢條件 PhraseQuery query = new PhraseQuery(); // 等同於 where contains("msg",kw) query.Add(new Term("msg", keyword)); // 兩個詞的距離大於100(經驗值)就不放入搜索結果,因為距離太遠相關度就不高了 query.SetSlop(100); // TopScoreDocCollector:盛放查詢結果的容器 TopScoreDocCollector collector = TopScoreDocCollector.create(1000, true); // 使用query這個查詢條件進行搜索,搜索結果放入collector searcher.Search(query, null, collector); // 從查詢結果中取出第m條到第n條的數據 // collector.GetTotalHits()表示總的結果條數 ScoreDoc[] docs = collector.TopDocs(0, collector.GetTotalHits()).scoreDocs; // 遍歷查詢結果 IList<SearchResult> resultList = new List<SearchResult>(); for (int i = 0; i < docs.Length; i++) { // 拿到文檔的id,因為Document可能非常占記憶體(DataSet和DataReader的區別) int docId = docs[i].doc; // 所以查詢結果中只有id,具體內容需要二次查詢 // 根據id查詢內容:放進去的是Document,查出來的還是Document Document doc = searcher.Doc(docId); SearchResult result = new SearchResult(); result.Id = Convert.ToInt32(doc.Get("id")); result.Msg = HighlightHelper.HighLight(keyword, doc.Get("msg")); resultList.Add(result); } // 綁定到Repeater rptSearchResult.DataSource = resultList; rptSearchResult.DataBind(); }搜索方法

protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { // 檢查是否已存在生成的索引文件 CheckIndexData(); } } /// <summary> /// 檢查索引是否創建成功 /// </summary> private void CheckIndexData() { string indexPath = Context.Server.MapPath("~/Index"); // 索引文檔保存位置 var files = System.IO.Directory.GetFiles(indexPath); if (files.Length > 0) { btnCreateIndex.Visible = false; lblIndexStatus.Text = "簡單搜索"; lblIndexStatus.Visible = true; } }檢查索引是否存在方法
運行效果如圖: