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包,才能正常編譯運行。

在編寫這個例子的時候,參考了網上其他朋友的文章及代碼,在此一併感謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章