最近奉命做一个全文检索程序,用关键字建立文件索引,类似如下结构:
词1 文章号1,文章号2,文章号3,文章号4...词2 文章号3,文章号4,文章号5,文章号6...词3 文章号1,文章号2,文章号8,文章号7.......有朋友骂我说为何不用lucene,我当然是提出了用,不过被否决了。只好挽起裤脚亲自下田了。这种存放方式,按照关键字可以快速找到词所在的所有文章,至于多关键字查询就要将结果作一个交集了。我原本打算用gdbm文件数据库存放索引(关系数据库对这种结构不太好用),找来找去发现java好像无法支持gdbm,我可不希望用jni去访问gdbm,不过好在我在sourceforge找到了jdbm可以代替使用。如果你的程序需要保存些简单的数据,又不想使用数据库,可以选择jdbm。另外附上二元分词法的程序,用来将文章分词,这个分词没有词表维护,实现简单,将就能用吧。
jdbm很简单,指定一个文件名,就可以当作一个hashtable来使用了:import jdbm.*;。。。JDBMRecordManager newsindex = new JDBMRecordManager(filename);JDBMHashtable hashtable = newsindex.getHashtable("words");。。。//建立索引方法,wordBreaker将文章敲碎成一个个词 public void index(String docid, String body) { try { wordBreaker.setText(body); String[] words = wordBreaker.breakAll(); for (int i = 0; i < words.length; i++) { if (words[i] != null && words[i].length() != 0) { String docs = (String) hashtable.get(words[i]); if (docs == null) { hashtable.put(words[i], docid); } else { if(docs.indexOf(docid)==-1){ hashtable.put(words[i], docid + "," + docs); } } }
}
} catch (Exception ex) { ex.printStackTrace(); } }
//输出所有数据看看,测试使用的方法 public void show(){ try { JDBMEnumeration enum = hashtable.keys(); while (enum.hasMoreElements()) { String aKey= (String)enum.nextElement(); String value= (String)hashtable.get(aKey); System.out.println(aKey+ ":" + value); } } catch (Exception ex) {
} }//记得关闭jdbm文件public void close(){ try { hashtable.dispose(); newsindex.close(); } catch (Exception ex) { } }另外附上二元分词程序WordBreaker:import java.text.*;import java.util.*;
public class WordBreaker { char[] punctuations = new char[] { '。', ',', ';', ':', '“', '”', '(', ')', '!', '?', '◎', '#', '¥', '%', '…', '※', '×', '【', '】', '『', '』', '《', '》', '、'}; public WordBreaker() { } public void setText(String text){ this.sourceText = text; } private String sourceText = ""; public String[] breakAll() { StringBuffer enWord = new StringBuffer(); StringBuffer cnWord = new StringBuffer(); boolean lastInsertCn = false; for (int i = 0; i < sourceText.length(); i++) { char c = sourceText.charAt(i); if (c > 255 && isWord(c)) { cnWord.append(c); lastInsertCn = true; } else if (c < 255) { if (lastInsertCn) { enWord.append(' '); enWord.append(c); lastInsertCn = false; } else { enWord.append(c); } } } String str = cnWord.toString(); String[] result = new String[str.length() - 1]; for (int i = 0; i < str.length() - 1; i++) { char c1 = str.charAt(i); char c2 = str.charAt(i + 1); result[i] = "" + c1 + c2; } String[] allEnWords = enWord.toString().split("//p{Punct}|//s+"); String[] ret = new String[allEnWords.length + result.length]; System.arraycopy(result,0,ret,0,result.length); System.arraycopy(allEnWords,0,ret,result.length,allEnWords.length); return ret; }
public static void main(String[] args) { WordBreaker wb = new WordBreaker(); wb.setText("在目前的80名发审委员的机制下,监管部门人员不足1/3,没有绝对话语权。发审委的工作等于是完全独立于监管部门之上的。从这个意义上,面对市场的诘责,证监会有替人受过之嫌。"); wb.printArray(wb.breakAll()); }
private void printArray(Object[] os) { for (int i = 0; i < os.length; i++) { System.out.println(os[i]);
}
} private boolean isWord(char c) { for (int i = 0; i < punctuations.length; i++) { if (c == punctuations[i]) { return false; } } return true; }}将文中单词按照2元语法(bigram)方式切分出来,比如:"北京天安门" ==> "北京 京天 天安 安门"。英语按照空格和标点分词。下载jdbm:jdbm.sf.net