- 下載示範工程 - 389 Kb
- 資料庫檔案 - 1.38 Kb
- 下載原始碼 - 111 Kb
簡介
如果你的網站需要或者程式需要一個文字搜尋引擎, 那麼除非你自己寫一個, 不然你就要把你想要搜尋的資料交給某一個網路搜尋引擎然後花錢來索引你的網站, 但是現在你用我的程式就可以索引你的 text/HTML/ASP 檔案了,它會將關鍵詞儲存到資料庫以備將來的搜尋之用.
程式使用 SQLite 作為資料庫. 它是一個開源的資料庫可以免費使用. 更多的資訊可以參考這個網站.
我使用 Finisar 的 .NET 封裝來使用 SQLite. 這個封裝實現了一個 SQLite的 ADO.NET 驅動, 而且用起來很順手. 它的 DLL 可以從 SourceForge下載.
資料庫設計
從中你可以看到資料庫的 E/R 鏈圖:
這張圖說明了 m<-> n 的關係, 根據這個關係我們把它轉化為Word_Page表(索引表).
建立資料庫 (class DBWSE)
我認為一個程式應該能夠自己建立資料庫那才算是一個完整的程式,也就是說它的可執行檔不需要帶著一個空的資料庫. 我們用 CREATE SQL 陳述式來建立所需要的表.
Collapse
private void CreateDataBase(string path)
{
try
{
FileStream fi = File.Create(path);
fi.Close();
DBFile = path;
OpenDataBase();
string strSQL = "Create Table words (" +
"WID INTEGER PRIMARY KEY ," +
"Word NVarChar(50)" +
")";
SQLiteCommand sqd = new SQLiteCommand(strSQL, sqconn);
sqd.ExecuteNonQuery();
sqd.CommandText ="Create Table pages(" +
"PID INTEGER PRIMARY KEY ," +
"path NVarChar(100) NOT NULL," +
"nofw INTEGER NOT NULL," +
"Date_add NVARChar(10) NOT NULL" +
")";
sqd.ExecuteNonQuery();
sqd.CommandText ="Create Table word_page(" +
"WID INTEGER FORIGEN KEY REFERENCES words (wid)," +
"PID INTEGER FORIGEN KEY REFERENCES pages (pid)," +
"QTY INTEGER NOT NULL," +
"PRIMARY KEY (WID,PID)" +
")";
sqd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
}
上面的代碼建立了一個空的資料庫檔案.
向Word, Page, 和 Word_Page表中添加資料
現在我們要把資料存到資料庫當中去. 為此需要使用 SQL 語言中的 INSERT 命令.
public int AddPage(string path, int nofw)<br>{<br> int i = PID(path);<br> if (i < 0)<br> {<br> // the page must be add <br> string strSQL = "INSERT INTO pages (path,nofw,Date_add)" +<br> "VALUES ('" + path + "'," + nofw + ",'" + <br> DateTime.Now.ToShortDateString() + "')";<br> SQLiteCommand sqc = new SQLiteCommand(strSQL, sqconn);<br> sqc.CommandText = strSQL;<br> sqc.ExecuteNonQuery();<br> //return PID<br> i = PID(path);<br> }<br> return i;<br>} |
"PID 函數" 用來返回新頁面的ID(page ID). 接下來我們把頁面的資料存到資料庫當中來.
private int createWord(string word)<br>{<br> string strsql = "INSERT INTO words (word) VALUES ('" + word + "')";<br> SQLiteCommand sqc = new SQLiteCommand(strsql, sqconn);<br> sqc.ExecuteNonQuery();<br> return WID(word);<br>} |
這個函數將向 "Word" 表中添加一個關鍵詞.
public void AddWord(int page, string word, int QTY)<br>{<br> int i = WID(word);<br> if (i < 0)<br> i = createWord(word);<br> string strsql = "INSERT INTO word_page (WID,PID,QTY) VALUES " +<br> "(" + i.ToString() + "," + page.ToString() + <br> "," + QTY.ToString() + ")";<br> SQLiteCommand sqc = new SQLiteCommand(strsql, sqconn);<br> sqc.ExecuteNonQuery();<br>} |
按照現在的設計關鍵詞是不能夠重複的. 當查詢某個詞的時候如果找到了則返回這個詞所對應的 ID (WID)否則返回 -1. 當然首先我們必須要把詞存入 Words 表中. 實際上這個函數在儲存頁面索引的時候並沒有儲存更多的冗餘的資料.
搜尋資料庫
現在我們可以在資料庫中搜尋某個詞了.
public DataTable SearchWord(string word)<br>{<br> string strSQL = "SELECT pages.path,pages.nofw,pages.Date_add,word_page.QTY " +<br> "FROM words INNER JOIN word_page ON words.wid=word_page.wid" +<br> " INNER JOIN pages ON word_page.pid=pages.pid" +<br> " WHERE words.word='"+word+"'";<br> SQLiteDataAdapter sqd = new SQLiteDataAdapter(strSQL, sqconn);<br> DataTable dt = new DataTable();<br> sqd.Fill(dt);<br> return dt;<br>} |
函數返回 DataTable 對象用來在程式中使用.
為儲存在資料庫中的資料建立索引
現在你已經把資料都儲存在資料庫當中了, 為了使用這些資料我們需要為這些頁面建立索引!
開始的時候這項工作並不難! 我們可以用一些分隔字元來把一段文章切分成詞. 例如: 空格 , ',' , ')' , '(' , '[' ,'\' 等等, 但是這麼做不是很精確.
你必須先去掉那些不重要的或者是重複的詞, 然後再開始建立索引.
拆分文章段落
string[] split = words.Split (new Char[] { ' ', ',', '.', ':',';','{','}','[',<br> ']','(',')','\'','"','\\','<','>','=','+','-','/','*','#','&','%','^',<br> '`','~','0','1','2','3','4','5','6','7','8','9' }); |
建立關鍵詞列表(class ScanFile) 摺疊
private void StartMonitoring(string p)
{
//first step must read file
StreamReader stR = File.OpenText(p);
string words = stR.ReadToEnd();
stR.Close();
string[] split = words.Split(new Char[] { ' ', ',', '.', ':',';','{','}','[',
']','(',')','\'','"','\\','<','>','=','+','-','/','*','#','&','%','^',
'`','~','0','1','2','3','4','5','6','7','8','9' });
max = split.Length;
int index;
int k = 0;
list1.Clear();
for (int i = 0; i < split.Length; i++)
{
WordInfo word = new WordInfo();
word.Word = split[i].Trim();
if (word.Word.Length > 2)
{
SearchableWord = word.Word;
index = list1.FindIndex(HaveWord);
if (index < 0)//not found
{
word++;
list1.Add(word);
k++;
}
else
{
// increment count of word
list1[index]++;
k++;
}
}
// Progress( this is a event )
OnProgress(System.EventArgs.Empty);
}
total = k;
}
private static bool HaveWord(WordInfo str)
{
if (str.Word.ToUpper() == SearchableWord.ToUpper())
return true;
else
return false;
}
在這個函數中我使用了 WordInfo 和 List 類, 並且為檢索檔案建立了一個新的事件 "OnProgress".
SCF.Progress += new EventHandler(SCF_Progress); |
為了找到 列表(List)中的詞, 我們需要定義一個委託:
private static bool HaveWord(WordInfo str) |
然而用這個參數我們還不能找到詞,為瞭解決這個問題, 我添加了一個用於字串比較的私人變數:
private static string SearchableWord; |
垃圾詞處理 (class RepluseTrivial)
為了排除那些沒有價值的詞, 你就必須有一個這些詞的列表. 我是考慮把這些詞存入資料庫, 這樣就可以使用 SQL 陳述式很方便的使用它們. 所以我建立了一個垃圾詞表, 使用 insert/delete 來添加刪除這些詞.
當你建立一個 RepluseTrivial 對象的時候, 你需要給它傳一個詞的列表然後調用 Repulse 函數 來去除那些沒有價值的詞.
public void Repluse()<br>{<br> List<wordinfo> temp = new List<wordinfo>();<br> for (int i = 0; i < list1.Count; i++)<br> if (!IsTrivial(list1[i].Word))<br> temp.Add(list1[i]);<br> list1 = temp;<br>}</wordinfo></wordinfo> |
函數中調用 IsTrivial 函數來檢查是否為垃圾詞.
如何使用My Code
現在你就有了一個剔除垃圾詞的列表和一個用來儲存這些詞的資料庫, 使用前面提到的演算法調用"search 函數"去搜尋吧. 當然這個函數還可以被進一步開發成像 Google 和 Microsoft 的引擎一樣去隱藏的索引. (效率上可沒法相比).
我們可以用一個進度條來顯示進度:
public Form1()<br>{<br> InitializeComponent();<br> SCF = new ScanFile();<br> SCF.Progress += new EventHandler(SCF_Progress);<br>}<br>void SCF_Progress(object sender, EventArgs e)<br>{<br> progressBar1.Maximum = SCF.MaxProgress;<br> progressBar1.Value++;<br>} |
建立一個剔除垃圾詞的列表:
private void button2_Click(object sender, EventArgs e)<br>{<br> progressBar1.Value = 0;<br> SCF.Scan(textBox1.Text);<br> label2.Text = "Total Word:"+SCF.Total.ToString();<br> List<wordinfo> list1=new List<wordinfo>(); <br> list1 = SCF.WordList;<br> string str = <br> Path.GetDirectoryName(Application.ExecutablePath)+ <br> "\\Trivial.db3";<br> RepluseTrivial rt = new RepluseTrivial(str);<br> list1 = rt.Repluse(list1);<br> listView1.Items.Clear();<br> for (int i = 0; i < list1.Count; i++)<br> {<br> ListViewItem li = new ListViewItem(new string[] { i.ToString(), <br> list1[i].Count.ToString(), list1[i].Word });<br> listView1.Items.Add(li);<br> }<br>}</wordinfo></wordinfo> |
儲存列表到資料庫:
private void button3_Click(object sender, EventArgs e)<br>{<br> string str = Path.GetDirectoryName(Application.ExecutablePath);<br> DBWSE db = new DBWSE(str+""\\WSEDB.db3");<br> int i=db.AddPage(textBox1.Text,SCF.Total);<br> progressBar1.Maximum = SCF.WordList.Count;<br> progressBar1.Value = 0;<br> for (int j=0;j < SCF.WordList.Count;j++)<br> {<br> db.AddWord(i, SCF.WordList[j].Word, SCF.WordList[j].Count);<br> progressBar1.Value++;<br> }<br>} |
你可以很容易的將查詢的結果顯示在一個 DataGridView 控制項中:
private void button7_Click(object sender, EventArgs e)<br>{<br> string str = Path.GetDirectoryName(Application.ExecutablePath);<br> DBWSE db = new DBWSE(str + "\\WSEDB.db3");<br> dataGridView2.DataSource = db.SearchWord(textBox3.Text);<br>} |
轉自:http://www.sqlitechina.org/html/1/20071221/147.html