Lucene是一款优秀的全文检索引擎的框架,提供了完整的查询引擎和索引引擎。由于Lucene自带的例子可以正常处理英文文件,但是中文的文件却不能正常处理。网上查了很多资料,很多人都在问这个问题,但是答案却是只字片语,没有针对这个问题提出一个完整的解决办法。经过一番摸索,终于解决了这个问题。关键之处在于读入文件时需要为文件字符流指定编码字符集,并且该字符集需要与文件的编码字符集一致,才能保证根据这些文件创建的索引后,文件的内容能被正确搜索。目前Lucene已经更新到了4.5.1,本文既可以作为最新版本的入门例子,有可以为解决中文文件搜索提供参考。
在D:/work/lucene/example放入测试的文件
D:/work/lucene/index01 为索引文件的存放路径
代码如下(基于Lucene4.5.1编写):
package com.hsdl.lucene;
import info.monitorenter.cpdetector.io.ASCIIDetector;
import info.monitorenter.cpdetector.io.CodepageDetectorProxy;
import info.monitorenter.cpdetector.io.JChardetFacade;
import info.monitorenter.cpdetector.io.ParsingDetector;
import info.monitorenter.cpdetector.io.UnicodeDetector;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.util.Version;
public class LuceneDemo {
private static String contentFieldName = "content";
public static void main(String[] args) {
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_45);
try {
String docPath = "D:/work/lucene/example";
String indexPath = "D:/work/lucene/index01";
//创建索引
createIndex(analyzer, indexPath, docPath);
//搜索
search(analyzer, indexPath, "中国");
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (LockObtainFailedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 创建索引
*
* @param analyzer
* @param indexPath
* @param docPath
* @throws IOException
* @throws CorruptIndexException
* @throws LockObtainFailedException
*/
private static void createIndex(Analyzer analyzer, String indexPath,
String docPath) throws IOException, CorruptIndexException,
LockObtainFailedException {
IndexWriter iwriter;
Directory directory = FSDirectory.open(new File(indexPath));
// 配置IndexWriterConfig
IndexWriterConfig iwConfig = new IndexWriterConfig(Version.LUCENE_45,
analyzer);
iwConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);
iwriter = new IndexWriter(directory, iwConfig);
File file = new File(docPath);
indexDocs(iwriter, file);
iwriter.close();
}
/**
* 搜索
*
* @param analyzer
* @param indexPath
* @param queryStr
* @throws CorruptIndexException
* @throws IOException
* @throws ParseException
*/
private static void search(Analyzer analyzer, String indexPath,
String queryStr) throws CorruptIndexException, IOException,
ParseException {
Directory directory = FSDirectory.open(new File(indexPath));
// 搜索过程**********************************
// 实例化搜索器
IndexReader ireader = DirectoryReader.open(directory);
IndexSearcher isearcher = new IndexSearcher(ireader);
// 使用QueryParser查询分析器构造Query对象
QueryParser qp = new QueryParser(Version.LUCENE_45, contentFieldName, analyzer);
qp.setDefaultOperator(QueryParser.AND_OPERATOR);
Query query = qp.parse(queryStr);
// 搜索相似度最高的5条记录
TopDocs topDocs = isearcher.search(query, 5);
System.out.println("命中:" + topDocs.totalHits);
// 输出结果
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
System.out.println(scoreDocs.length);
for (int i = 0; i < scoreDocs.length; i++) {
Document targetDoc = isearcher.doc(scoreDocs[i].doc);
System.out.println("内容:" + targetDoc.toString());
System.out.println(targetDoc.get("fileName") + "["
+ targetDoc.get("path") + "]");
}
}
/**
* 根据指定存放内容的文件或目录创建索引
* @param iwriter
* @param file
* @throws IOException
*/
public static void indexDocs(IndexWriter iwriter, File file) throws IOException {
if (file.canRead())
if (file.isDirectory()) {
String[] files = file.list();
if (files != null)
for (int i = 0; i < files.length; i++)
indexDocs(iwriter, new File(file, files[i]));
} else {
Document doc = null;
FileInputStream fis=null;
try {
doc = new Document();
doc.add(new StringField("ID", "10000", Field.Store.YES));
fis = new FileInputStream(file);
System.out.println(getFileCharset(file));
doc.add(new TextField(contentFieldName, new BufferedReader(
new InputStreamReader(fis, getFileCharset(file)))));
doc.add(new StringField("fileName", file.getName(),
Field.Store.YES));
doc.add(new StringField("path", file.getAbsolutePath(),
Field.Store.YES));
iwriter.addDocument(doc);
} finally {
if(fis!=null){
fis.close();
}
}
}
}
/**
* 获取文件的编码字符集
* @param file
* @return
*/
public static String getFileCharset(File file) {
/*
* detector是探测器,它把探测任务交给具体的探测实现类的实例完成。
* cpDetector内置了一些常用的探测实现类,这些探测实现类的实例可以通过add方法 加进来,如ParsingDetector、
* JChardetFacade、ASCIIDetector、UnicodeDetector。
* detector按照“谁最先返回非空的探测结果,就以该结果为准”的原则返回探测到的
* 字符集编码。使用需要用到三个第三方JAR包:antlr.jar、chardet.jar和cpdetector.jar
* cpDetector是基于统计学原理的,不保证完全正确。
*/
CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
/*
* ParsingDetector可用于检查HTML、XML等文件或字符流的编码,构造方法中的参数用于
* 指示是否显示探测过程的详细信息,为false不显示。
*/
detector.add(new ParsingDetector(false));
// ASCIIDetector用于ASCII编码测定
detector.add(ASCIIDetector.getInstance());
// UnicodeDetector用于Unicode家族编码的测定
detector.add(UnicodeDetector.getInstance());
/*
* JChardetFacade封装了由Mozilla组织提供的JChardet,它可以完成大多数文件的编码
* 测定。所以,一般有了这个探测器就可满足大多数项目的要求,如果你还不放心,可以
* 再多加几个探测器,比如上面的ASCIIDetector、UnicodeDetector等。需要把这个放在最后添加。
*/
detector.add(JChardetFacade.getInstance());
java.nio.charset.Charset charset = null;
try {
charset = detector.detectCodepage(file.toURI().toURL());
} catch (Exception ex) {
ex.printStackTrace();
}
if (charset != null){
/*
* 如果编码是"windows-1252",将其设置为"GB2312",因为使用的环境为中国,
* 一般的文档也是在中文的Windows的环境下创建
*/
if(charset.name().equals("windows-1252")){
return "GB2312";
}
return charset.name();
}
else
return null;
}
}
在运行该例子时除了需要Lucene4.5相关的jar包外,还需要antlr.jar、cpdetector_1.0.8.jar这两个jar包,才能正常编译运行。
在编写这个例子的时候,参考了网上其他朋友的文章及代码,在此一并感谢!