Lucene30.docx
《Lucene30.docx》由会员分享,可在线阅读,更多相关《Lucene30.docx(23页珍藏版)》请在冰点文库上搜索。
Lucene30
分词原理
建立索引和查询的过程中,都是以基本的语素项为单位的。
基本的语素项就是通过分词得到。
这个过程决定了索引单元金额最终的匹配过程。
分词在文本索引的建立过程和用户提交检索过程中都存在。
利用相同的分词器,把短语或者句子切分成相同的结果,才能保证检索过程顺利进行。
1、英文分词的原理
基本的处理流程是:
输入文本、词汇分割、词汇过滤(去除停留词)、词干提取(形态还原)、大写转为小写、结果输出。
2、中文分词原理
中文分词比较复杂,并没有英文分词那么简单。
这主要是因为中文的词与词之间并不像英文中那样用空格来隔开。
主要的方法有三种:
基于词典匹配的分词方法、基于语义理解的分词、基于词频统计的分词。
① 基于词典匹配的分词方法
基于字典匹配的分词方法按照一定的匹配策略将输入的字符串与机器字典词条进行匹配。
如果在词典中找到当前字符串,则匹配成功输出识别的词汇。
按照匹配操作的扫描方向不同,字典匹配分词方法可以分为正向匹配和逆向匹配,以及结合了两者的双向匹配算法;按照不同长度优先匹配的情况,可以分为最大(最长)匹配和最小(最短)匹配;按照是否与词性标注过程相结合,又可以分为单纯分词方法和分词与词性标注相结合的方法。
几种常用的词典分词方法如下所示:
●正向最大匹配(由左到右的方向)。
●逆向最大匹配(由右到左的方向)。
●最少切分(是每一句中切除的词数最小)。
实际应用中上述各种方法经常组合使用,达到最好的效果,从而衍生出了结合正向最大匹配方法和逆向最大匹配算法的双向匹配分词法。
由于中分词最大的问题是歧义处理,结合中文语言自身的特点,经常采用逆向匹配的切分算法,处理的精度高于正向匹配,产生的切分歧义现象也较少。
真正实用的分词系统,都是把词典分词作为基础手段,结合各种语言的其他特征信息来提高切分的效果和准确度。
有的实用系统中将分词和词性标注结合起来,利用句法和词法分析对分词决策提高帮助,在词性标注过程中迭代处理,利用词性和语法信息对分词结果进行检验、调整。
既然是基于分词词典的分词方法,当然还要考虑选择一个好的查找词典算法。
一般有数字搜索树,Trie算法等等。
所谓的数字搜索树(retrieve树)就是要求树的一个节点保留一个字符。
如果一个单词比一个字符长,则包含第一个字符串的节点有指向下一个字符的节点,以此类推。
这样组成一个层次结构的树,树的第一层包括所有单词的第一个字符,树的第二层包括所有单词的第二个字符,以此类推,数字搜索树的最大高度是词典中最长单词的长度。
但是这样的树,每一个节点都需要很多内存。
假设每个词都是由一个小写英文字母组成的,这个节点中会有26个指针。
所以不太可能直接用这样的数字搜索树来存储中文这样的大字符集。
Trie树,即三叉搜索树,每一个节点包括一个字符,但只有三个指针,一个指向左边的树,一个指向右边的树,还有一个向下,指向单词的下一个数据单元。
三叉搜索树是二叉搜索树和数字搜索树的混合体。
它有和数字搜索树差不多的速度但是和二叉搜索树一样只需要相对较少的内存空间。
单词的读入顺序对于创建平衡的三叉搜索树很重要,但对于二叉搜索树就不是太重要了。
由于限于篇幅,这里就不写上代码了。
② 基于语义理解的分词
基于语义理解的分词方法是模拟人脑对语言和句子的理解,达到识别词汇单元的效果。
基本模式是把分词、句法、语义分析并行进行,利用句法和语义信息来处理分词的歧义。
一般结构中通常包括分词子系统、句法语义子系统、调度系统。
在调度系统的协调下,分词子系统可以获得有关词、句子等的句法和语义信息,模拟人脑对句子的理过程。
基于语义理解的分词方法需要使用大量的语言知识和信息。
目前国内外对汉语语言知识的理解和处理能力还没有达到语义层面,具体到语言信息很难组织成机器可直接读取、计算的形式,因此目前基于语义理解的分词系统还处在试验阶段。
③ 基于词频统计的分词
这种做法基于人们对中文词语的直接感觉。
通常词是稳定的词的组合,因此在中文文章的上下文中,相邻的字搭配出现的频率越多,就越有可能形成一个固定的词。
根据n元语法知识可以知道,字与字相邻同时出现的频率或概率能够较好地反映成词的可信度。
实际的系统中,通过对精心准备的中文语料中相邻共现的各个字的组合的频度进行统计,计算不同字词的共现信息。
根据两个字的统计信息,计算两个汉字的相邻共现概率。
统计处来的信息体现了中文环境下汉字之间结合紧密程度。
当紧密程度高于某一个阈值时,便可认为此字组可能构成一个词。
基于词频统计的分词方法只需要对语料中的字组频度进行统计,不需要切分词典,因而又叫做无词典分词法或统计分词方法。
这种方法经常抽出一些共现频度高、但并不是词的常用字组,需要专门处理,提高精确度。
实际应用的统计分词系统都使用一个基本的常用词词典,把字典分词和统计分词结合使用。
基于统计的方法能很好地解决词典未收录新词的处理问题,即将中文分词中的串频统计和串匹配结合起来,既发挥匹配分词切分速度快、效率高的特点,有利用了无词典分词结合上下文识别生词、自动消除歧义的优点。
3、Lucene分词
在Lucene3.0中,对分词主要依靠Analyzer类解析实现。
Analyzer内部主要通过TokenStream类实现。
Tonkenizer类,TokenFilter类是TokenStream的两个子类。
Tokenizer处理单个字符组成的字符流,读取Reader对象中的数据,处理后转换成词汇单元。
TokneFilter完成文本过滤器的功能,但在使用过程中必须注意不同的过滤器的使用的顺序。
Lucene是apache组织的一个用java实现全文搜索引擎的开源项目。
其功能非常的强大,api也很简单。
总得来说用Lucene来进行建立和搜索和操作数据库是差不多的(有点像),Document可以看作是数据库的一行记录,Field可以看作是数据库的字段。
用lucene实现搜索引擎就像用JDBC实现连接数据库一样简单。
Lucene2.0,它与以前广泛应用和介绍的Lucene1.4.3并不兼容。
Lucene2.0的下载地址是http:
//apache.justdn.org/lucene/java/
例子一:
1、在windows系统下的的C盘,建一个名叫s的文件夹,在该文件夹里面随便建三个txt文件,随便起名啦,就叫"1.txt","2.txt"和"3.txt"啦
其中1.txt的内容如下:
中华人民共和国
全国人民
2006年
而"2.txt"和"3.txt"的内容也可以随便写几写,这里懒写,就复制一个和1.txt文件的内容一样吧
2、下载lucene包,放在classpath路径中
建立索引:
package;
importjava.io.BufferedReader;
importjava.io.File;
importjava.io.FileInputStream;
importjava.io.IOException;
importjava.io.InputStreamReader;
importjava.util.Date;
importorg.apache.lucene.analysis.Analyzer;
importorg.apache.lucene.analysis.standard.StandardAnalyzer;
importorg.apache.lucene.document.Document;
importorg.apache.lucene.document.Field;
importorg.apache.lucene.index.IndexWriter;
/***//**
*authorlighterdate2006-8-7
*/
public classTextFileIndexer{
public static voidmain(String[]args)throwsException{
/**//*指明要索引文件夹的位置,这里是C盘的S文件夹下*/
FilefileDir= newFile("c:
\\s");
/**//*这里放索引文件的位置*/
FileindexDir= newFile("c:
\\index");
AnalyzerluceneAnalyzer= newStandardAnalyzer();
IndexWriterindexWriter= newIndexWriter(indexDir,luceneAnalyzer,
true);
File[]textFiles=fileDir.listFiles();
longstartTime= newDate().getTime();
//增加document到索引去
for(inti= 0;i if(textFiles[i].isFile()
&&textFiles[i].getName().endsWith(".txt")){
System.out.println("File" +textFiles[i].getCanonicalPath()
+ "正在被索引.");
Stringtemp=FileReaderAll(textFiles[i].getCanonicalPath(),
"GBK");
System.out.println(temp);
Documentdocument= newDocument();
FieldFieldPath= newField("path",textFiles[i].getPath(),
Field.Store.YES,Field.Index.NO);
FieldFieldBody= newField("body",temp,Field.Store.YES,
Field.Index.TOKENIZED,
Field.TermVector.WITH_POSITIONS_OFFSETS);
document.add(FieldPath);
document.add(FieldBody);
indexWriter.addDocument(document);
}
}
//optimize()方法是对索引进行优化
indexWriter.optimize();
indexWriter.close();
//测试一下索引的时间
longendTime= newDate().getTime();
System.out
.println("这花费了"
+(endTime-startTime)
+ "毫秒来把文档增加到索引里面去!
"
+fileDir.getPath());
}
public staticStringFileReaderAll(StringFileName,Stringcharset)
throwsIOException{
BufferedReaderreader= newBufferedReader(newInputStreamReader(
newFileInputStream(FileName),charset));
Stringline= newString();
Stringtemp= newString();
while((line=reader.readLine())!
= null){
temp+=line;
}
reader.close();
returntemp;
}
}
索引的结果:
FileC:
\s\1.txt正在被索引.
中华人民共和国全国人民2006年
FileC:
\s\2.txt正在被索引.
中华人民共和国全国人民2006年
FileC:
\s\3.txt正在被索引.
中华人民共和国全国人民2006年
这花费了297毫秒来把文档增加到索引里面去!
c:
\s
3、建立了索引之后,查询啦....
package;
importjava.io.IOException;
importorg.apache.lucene.analysis.Analyzer;
importorg.apache.lucene.analysis.standard.StandardAnalyzer;
importorg.apache.lucene.queryParser.ParseException;
importorg.apache.lucene.queryParser.QueryParser;
importorg.apache.lucene.search.Hits;
importorg.apache.lucene.search.IndexSearcher;
importorg.apache.lucene.search.Query;
public classTestQuery{
public static voidmain(String[]args)throwsIOException,ParseException{
Hitshits= null;
StringqueryString= "中华";
Queryquery= null;
IndexSearchersearcher= newIndexSearcher("c:
\\index");
Analyzeranalyzer= newStandardAnalyzer();
try {
QueryParserqp= newQueryParser("body",analyzer);
query=qp.parse(queryString);
} catch(ParseExceptione){
}
if(searcher!
= null){
hits=searcher.search(query);
if(hits.length()> 0){
System.out.println("找到:
" +hits.length()+ "个结果!
");
}
}
}
}
其运行结果:
找到:
3个结果!
Lucene其实很简单的,它最主要就是做两件事:
建立索引和进行搜索
来看一些在lucene中使用的术语,这里并不打算作详细的介绍,只是点一下而已----因为这一个世界有一种好东西,叫搜索。
IndexWriter:
lucene中最重要的的类之一,它主要是用来将文档加入索引,同时控制索引过程中的一些参数使用。
Analyzer:
分析器,主要用于分析搜索引擎遇到的各种文本。
常用的有StandardAnalyzer分析器,StopAnalyzer分析器,WhitespaceAnalyzer分析器等。
Directory:
索引存放的位置;lucene提供了两种索引存放的位置,一种是磁盘,一种是内存。
一般情况将索引放在磁盘上;相应地lucene提供了FSDirectory和RAMDirectory两个类。
Document:
文档;Document相当于一个要进行索引的单元,任何可以想要被索引的文件都必须转化为Document对象才能进行索引。
Field:
字段。
IndexSearcher:
是lucene中最基本的检索工具,所有的检索都会用到IndexSearcher工具;
Query:
查询,lucene中支持模糊查询,语义查询,短语查询,组合查询等等,如有TermQuery,BooleanQuery,RangeQuery,WildcardQuery等一些类。
QueryParser:
是一个解析用户输入的工具,可以通过扫描用户输入的字符串,生成Query对象。
Hits:
在搜索完成之后,需要把搜索结果返回并显示给用户,只有这样才算是完成搜索的目的。
在lucene中,搜索的结果的集合是用Hits类的实例来表示的。
上面作了一大堆名词解释,下面就看几个简单的实例吧:
1、简单的的StandardAnalyzer测试例子
package;
importjava.io.IOException;
importjava.io.StringReader;
importorg.apache.lucene.analysis.Analyzer;
importorg.apache.lucene.analysis.Token;
importorg.apache.lucene.analysis.TokenStream;
importorg.apache.lucene.analysis.standard.StandardAnalyzer;
public classStandardAnalyzerTest
{
//构造函数,
publicStandardAnalyzerTest()
{
}
public static voidmain(String[]args)
{
//生成一个StandardAnalyzer对象
AnalyzeraAnalyzer= newStandardAnalyzer();
//测试字符串
StringReadersr= newStringReader("lighterjavaeyecomistheareon");
//生成TokenStream对象
TokenStreamts=aAnalyzer.tokenStream("name",sr);
try {
inti=0;
Tokent=ts.next();
while(t!
=null)
{
//辅助输出时显示行号
i++;
//输出处理后的字符
System.out.println("第"+i+"行:
"+t.termText());
//取得下一个字符
t=ts.next();
}
} catch(IOExceptione){
e.printStackTrace();
}
}
}
显示结果:
第1行:
lighter
第2行:
javaeye
第3行:
com
提示一下:
StandardAnalyzer是lucene中内置的"标准分析器",可以做如下功能:
1、对原有句子按照空格进行了分词
2、所有的大写字母都可以能转换为小写的字母
3、可以去掉一些没有用处的单词,例如"is","the","are"等单词,也删除了所有的标点
查看一下结果与"newStringReader("lighterjavaeyecomistheareon")"作一个比较就清楚明了。
这里不对其API进行解释了,具体见lucene的官方文档。
需要注意一点,这里的代码使用的是lucene2的API,与1.