简介
如果你的网站需要或者程序需要一个文字搜索引擎, 那么除非你自己写一个, 不然你就要把你想要搜索的数据交给某一个网络搜索引擎然后花钱来索引你的网站, 但是现在你用我的程序就可以索引你的 text/HTML/ASP 文件了,它会将关键词保存到数据库以备将来的搜索之用.
程序使用 SQLite 作为数据库. 它是一个开源的数据库可以免费使用. 更多的信息可以参考这个网站.
我使用 Finisar 的 .NET 封装来使用 SQLite. 这个封装实现了一个 SQLite的 ADO.NET 驱动, 而且用起来很顺手. 它的 DLL 可以从 SourceForge下载.
using Finisar.SQLite;
数据库设计
从下图中你可以看到数据库的 E/R 链图:
这张图说明了 m<-> n 的关系, 根据这个关系我们把它转化为Word_Page表(索引表).
Word_Page | ||
Wid | Pid | QTY |
创建数据库 (class DBWSE)
我认为一个程序应该能够自己创建数据库那才算是一个完整的程序,也就是说它的可执行文件不需要带着一个空的数据库. 我们用 CREATE
SQL 语句来创建所需要的表.
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)
{
int i = PID(path);
if (i < 0)
{
// the page must be add
string strSQL = "INSERT INTO pages (path,nofw,Date_add)" +
"VALUES ('" + path + "'," + nofw + ",'" +
DateTime.Now.ToShortDateString() + "')";
SQLiteCommand sqc = new SQLiteCommand(strSQL, sqconn);
sqc.CommandText = strSQL;
sqc.ExecuteNonQuery();
//return PID
i = PID(path);
}
return i;
}
"PID 函数" 用来返回新页面的ID(page ID). 接下来我们把页面的数据存到数据库当中来.
private int createWord(string word)
{
string strsql = "INSERT INTO words (word) VALUES ('" + word + "')";
SQLiteCommand sqc = new SQLiteCommand(strsql, sqconn);
sqc.ExecuteNonQuery();
return WID(word);
}
这个函数将向 "Word" 表中添加一个关键词.
public void AddWord(int page, string word, int QTY)
{
int i = WID(word);
if (i < 0)
i = createWord(word);
string strsql = "INSERT INTO word_page (WID,PID,QTY) VALUES " +
"(" + i.ToString() + "," + page.ToString() +
"," + QTY.ToString() + ")";
SQLiteCommand sqc = new SQLiteCommand(strsql, sqconn);
sqc.ExecuteNonQuery();
}
按照现在的设计关键词是不能够重复的. 当查询某个词的时候如果找到了则返回这个词所对应的 ID (WID)
否则返回 -1. 当然首先我们必须要把词存入 Words 表中. 实际上这个函数在储存页面索引的时候并没有保存更多的冗余的数据.
搜索数据库
现在我们可以在数据库中搜索某个词了.
public DataTable SearchWord(string word)
{
string strSQL = "SELECT pages.path,pages.nofw,pages.Date_add,word_page.QTY " +
"FROM words INNER JOIN word_page ON words.wid=word_page.wid" +
" INNER JOIN pages ON word_page.pid=pages.pid" +
" WHERE words.word='"+word+"'";
SQLiteDataAdapter sqd = new SQLiteDataAdapter(strSQL, sqconn);
DataTable dt = new DataTable();
sqd.Fill(dt);
return dt;
}
函数返回 DataTable
对象用来在程序中使用.
为保存在数据库中的数据建立索引
现在你已经把数据都保存在数据库当中了, 为了使用这些数据我们需要为这些页面建立索引!
开始的时候这项工作并不难! 我们可以用一些分隔符来把一段文章切分成词. 例如: 空格 , ',' , ')' , '(' , '[' ,'\' 等等, 但是这么做不是很精确.
你必须先去掉那些不重要的或者是重复的词, 然后再开始建立索引.
拆分文章段落
string[] split = words.Split (new Char[] { ' ', ',', '.', ':',';','{','}','[',
']','(',')','\'','"','\\','<','>','=','+','-','/','*','#','&','%','^',
'`','~','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()
{
List temp = new List();
for (int i = 0; i < list1.Count; i++)
if (!IsTrivial(list1[i].Word))
temp.Add(list1[i]);
list1 = temp;
}
函数中调用 IsTrivial
函数来检查是否为垃圾词.
如何使用我的代码
现在你就有了一个剔除垃圾词的列表和一个用来存储这些词的数据库, 使用前面提到的算法调用"search 函数"去搜索吧. 当然这个函数还可以被进一步开发成像 Google 和 Microsoft 的引擎一样去隐藏的索引. (效率上可没法相比).
我们可以用一个进度条来显示进度:
public Form1()
{
InitializeComponent();
SCF = new ScanFile();
SCF.Progress += new EventHandler(SCF_Progress);
}
void SCF_Progress(object sender, EventArgs e)
{
progressBar1.Maximum = SCF.MaxProgress;
progressBar1.Value++;
}
创建一个剔除垃圾词的列表:
private void button2_Click(object sender, EventArgs e)
{
progressBar1.Value = 0;
SCF.Scan(textBox1.Text);
label2.Text = "Total Word:"+SCF.Total.ToString();
List list1=new List();
list1 = SCF.WordList;
string str =
Path.GetDirectoryName(Application.ExecutablePath)+
"\\Trivial.db3";
RepluseTrivial rt = new RepluseTrivial(str);
list1 = rt.Repluse(list1);
listView1.Items.Clear();
for (int i = 0; i < list1.Count; i++)
{
ListViewItem li = new ListViewItem(new string[] { i.ToString(),
list1[i].Count.ToString(), list1[i].Word });
listView1.Items.Add(li);
}
}
保存列表到数据库:
private void button3_Click(object sender, EventArgs e)
{
string str = Path.GetDirectoryName(Application.ExecutablePath);
DBWSE db = new DBWSE(str+""\\WSEDB.db3");
int i=db.AddPage(textBox1.Text,SCF.Total);
progressBar1.Maximum = SCF.WordList.Count;
progressBar1.Value = 0;
for (int j=0;j < SCF.WordList.Count;j++)
{
db.AddWord(i, SCF.WordList[j].Word, SCF.WordList[j].Count);
progressBar1.Value++;
}
}
你可以很容易的将查询的结果显示在一个 DataGridView
控件中:
private void button7_Click(object sender, EventArgs e)
{
string str = Path.GetDirectoryName(Application.ExecutablePath);
DBWSE db = new DBWSE(str + "\\WSEDB.db3");
dataGridView2.DataSource = db.SearchWord(textBox3.Text);
}
FilesTube: 共享文件搜索引擎,文件来自:Rapidshare, MegaUpload, Megashares, YouSendIt, SaveFile, FileFront和Badongo等很多文件储存网站,支持的文件格式包括:AVI, MP3, MPEG, MPG, RAR, WMA, WMV, EXE, ZIP等,主要为媒体格式,不支持中文
NiuDown:国内垂直搜索,专注于对Doc、Exl、PPT及PDF文档的搜索
Searchme:可视化搜索引擎 (Visual Search Engine),基于FLEX技术,很精美,很华丽
Mp3Realm:MP3搜索引擎,提供下载、试听、歌词、加入播放列表(注册用户)
pdf-search-engine :pdf文档搜索引擎,搜索结果基本来自Google,只支持英文pdf搜索
MP3ZY: 搜索英文音频文件并提供试听和下载
Bemp3:提供音乐的搜索,试听,和下载,具有可参加评论等功能
Musgle:利用Google搜索音乐文件和信息
SeeqPod:音乐搜索和播放 提供API 全站flash制作 整理搜索出来的列表可嵌去他处
SkreemR:只搜mp3,下载,出处,Lyricwiki的歌词、Wikipedia的歌手信息、YouTube的音乐视频、Amazon的CD、StubHub的音乐会票务和Flickr的歌手相片快捷链接
Woonz:提供垂直搜索音乐的视听和下载
Songza:专业音乐搜索引擎,完美支持中文,提供可嵌入的HTML代码,有投票机制
Yotophoto:图片搜索,能够按指定颜色搜索,不支持中文,图片类型包括照片、插图、绘画、图标、图表、表格、工程图、地图、三维图等,高级搜索对照片形状,关键字(完全匹配,任意匹配),照片授权许可范围证书的选择进行分类
Tooooold:FTP搜索
SimpleSpark:网络软件搜索,垂直搜索,无高级检索的功能,无蜘蛛爬虫
Dorble:mp3搜索引擎,专辑推荐采用了Apple的卷叶技术
WuZAM:MP3搜索,下载,可在线试听
FotoViewr: 利用Flickr的api提供四种方法搜索并浏览Flickr上的图片,很有意思的服务.其中也包括了Leopard下的Flow效果