Lucene学习之Lucene入门暨中文文件搜索有关问题的解决
Lucene学习之Lucene入门暨中文文件搜索问题的解决
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包,才能正常编译运行。
在编写这个例子的时候,参考了网上其他朋友的文章及代码,在此一并感谢!