文本检索 关键词检索和
问候,
欢迎回来; 上面我们讨论了Library类的外围设备:loading
并保存它的实例化,然后在BookMark接口中保存。
本文的这一部分将进一步讨论Library类的内部。
再次分段上一篇文章的一部分显示了Sections是如何工作的,即组Section所指
预订科目,一本书科目指的是科目章节,后者指的是
到文字段落。
在Library类中实现了一些方法,可以处理所有
区段操纵。 这是前两个; 他们找到一个部分的索引
在给定Section名称的Section数组中:
private int getIndex(Section[] sections, String name) {
return getIndex(sections, name, 0, sections.length);
}
private int getIndex(Section[] sections, String name, int lo, int n) {
for (int i= lo; n-- > 0; i++)
if (sections[i].getName().equals(name)) return i;
return -1;
}
第一种方法在给定的Sections数组中找到Section的索引
本节的名称。 它将工作委托给第二种方法
采用最低索引号形式的索引间隔为
考虑和数组中相邻节的数量。
这几乎不是火箭科学,但是我们仍然需要这些方法。 下一个
方法更聪明:假设我们要查找给定书的索引
一章的绝对索引号; 以下方法可以处理所有问题:
private int getIndex(Section[] section, int index) {
for (int lo= 0, hi= section.length-1; lo <= hi; ) {
int mid= (lo+hi)>>>1;
int ilo= section[mid].getIndex();
int ihi= ilo+section[mid].getTotal();
if (ilo <= index && ihi > index) return mid;
if (ihi <= index) lo= mid+1;
else hi= mid-1;
}
return -1;
}
给定一个Sections数组和一个索引号,此方法将应用二进制
在数组中搜索并找到“包容”部分的索引
所需索引,即Section包含作为参数传入的索引。
例如,詹姆士国王圣经包含1352章和36133段。
给定一个段落编号,此方法可以找到相应的章节
在2log(1352)操作中,最多少于11个搜索操作。
鉴于
章节等的索引,因为书籍远远少于那里
是章节,而书籍则少得多。
信不信由你,但上述方法是唯一的“智能”
需要并在Library类中实现。
简单统计库对象也可以生成简单的统计信息以及
实施起来非常简单; 他们来了:
public String getTitle() { return title; }
public int getGroupSize() { return groups.length; }
public int getBookSize() { return books.length; }
public int getChapterSize() { return chapters.length; }
public int getParagraphSize() { return paragraphs.length; }
public int getWordSize() { return words.length; }
public int getIndexSize() { return wordMap.size(); }
public int getOccurrences(String word) {
int[] paragraphs= wordMap.get(word);
return (paragraphs == null)?0:paragraphs.length;
}
第一种方法实际上不是统计类型的方法,但我将其包括在内
无论如何。 其他方法简单地返回各种数组的长度,并且
WordMap在文本检索功能中起着至关重要的作用。
这是我实现的另一种简单方法:
public int[] getIndexes(String word) {
int[] paragraphs= wordMap.get(word);
return (paragraphs == null)?null:Arrays.copyOf(paragraphs, paragraphs.length);
}
该方法返回出现某个单词时所有索引值的副本。
因为我不希望任何调用者返回原始数组的副本
不小心破坏了原始数组中的值。 有一个来电者
我相信:我; *啊*,所以特别是对我来说,我写了这个方法:
int[] getWordIndexes(String word) { return wordMap.get(word); }
稍后,当我们要实现更多精美的文本时,我需要那种方法
检索功能。 请注意,这不是公共方法。
文字解码如前一篇文章所述,文本是经过压缩和编码的。
段落字符串不是文字文本,而只是其表示形式。
以下(非公开!)方法再次检索实际文本,因为
编码的段落字符串:
String decompress(String text) {
StringBuffer sb= new StringBuffer();
for (int i= 0, n= text.length(); i < n; i++) {
char c= text.charAt(i);
if (c < 0x20)
sb.append(punctuation[0].charAt(c)).append(' ');
else if (c < 0x100)
sb.append(punctuation[1].charAt(c&0x1f));
else {
sb.append(words[c-0x100]);
if (i < n-1 && text.charAt(i+1) >= 0x100)
sb.append(' ');
}
}
return sb.toString();
}
如果“字符串”段落中的字符代表标点之一
字符将被翻译回,并在需要时添加空格。
否则,该字符表示单词数组中的索引; 这个词是
如果跟随当前单词,则检索并可选地添加空格
换句话说。 再次比较该方法中的'compress()'方法
上一篇文章的部分:它只是执行反向操作。
简单的文本检索在深入研究真正的检索之前,这里有一些简单的功能:
库类为此使用了一个非常简单的类,即SectionList类。
这是部分的列表,或者在Java中是:List <String>。 这是这样做的:
class SectionList extends AbstractList<String> {
private Section[] section;
private int lo, n;
SectionList(Section[] section, int lo, int n) {
this.section= section;
this.lo = lo;
this.n = n;
}
public String get(int i) { return section[i+lo].getName(); }
public int size() { return n; }
}
构造函数采用一个Sections数组,一个“ lo”索引和
相邻节,这也是列表本身的长度。 清单有
String类型的值; 从各个节中检索字符串
他们自己。 请注意,AbstractList <String>超类负责
使该对象成为完整列表的其余功能。
以下所有方法都利用此小类:
public List<String> getGroups() {
return new SectionList(groups, 0, groups.length);
}
public List<String> getBooks() {
return new SectionList(books, 0, books.length);
}
public List<String> getBooks(String group) {
int i= getIndex(groups, group);
return (i < 0)?null:new SectionList(books, groups[i].getIndex(), groups[i].getTotal());
}
public List<String> getChapters() {
return new SectionList(chapters, 0, chapters.length);
}
public List<String> getChapters(String book) {
int i= getIndex(books, book);
return (i < 0)?null:new SectionList(chapters, books[i].getIndex(), books[i].getTotal());
}
请注意,大多数方法如何使用上文所述的'getIndex()'方法
为了获取Section数组之一中的索引范围。 字符串列表
照顾其余的。 真的就是这么简单。 最后一个方法返回一个
包含整个文本中所有唯一词但不包含任何唯一词的列表
特定顺序。
下一个小方法返回所有唯一单词的不可修改列表
以不特定的顺序排列,以防万一您对这样的列表感兴趣:
public List<String> getWords() {
return Collections.unmodifiableList(Arrays.asList(words));
}
考虑到我们的示例库存储了
詹姆士国王圣经文字:
Library lib= Library.load("/usr/jos/tmp/kj.txtlib"); // get the library
// show all books:
List<String> books= lib.getBooks();
for (String book : books)
System.out.println(book);
// show all chapters in book "Genesis"
List<String> chapters= lib.getChapters("Genesis");
for (String chapter : chapters)
System.out.println(chapter);
对于实际的段落文本,我们不能使用此StringList,而只是定义
另一个小类:BookMark类。 我们还需要另一种方法
库类本身:
BookMark getBookMark(int paragraph) { return new BookMarkImpl(paragraph); }
我们不知道什么是BookMarkImpl类,但可以肯定的是它是一个实现
书签界面。 我将在稍后解释此实现。 这是我们的
书签列表:
class BookMarkList extends AbstractList<BookMark> {
private Library lib;
private int lo, n;
BookMarkList(Library lib, int lo, int n) {
this.lib= lib;
this.lo = lo;
this.n = n;
}
public BookMark get(int i) { return lib.getBookMark(i+lo); }
public int size() { return n; }
}
请注意,该类对BookMarkImpl类一无所知,即
只对书签(界面)感兴趣。 类似于我们之前的清单
类,它为辛苦的工作实现了AbstractList,并返回一个
为其“ get()”方法添加书签。 我们相信神秘的BookMarkImpl类
实施适当的“ toString()”。
以下是使用这些类的Library类中的方法:
public List<BookMark> getParagraphs() {
return new BookMarkList(this, 0, paragraphs.length);
}
public List<BookMark> getParagraphs(String book) {
int i= getIndex(books, book);
if (i < 0) return null;
int clo= books[i].getIndex();
int chi= books[i].getTotal()+clo-1;
int p= chapters[clo].getIndex();
int n= chapters[chi].getIndex()+chapters[chi].getTotal()-p;
return new BookMarkList(this, p, n);
}
public List<BookMark> getParagraphs(String book, String chapter) {
int i= getIndex(books, book);
if (i < 0) return null;
int c= getIndex(chapters, chapter, books[i].getIndex(), books[i].getTotal());
return (c < 0)?null:new BookMarkList(this, chapters[c].getIndex(), chapters[c].getTotal());
}
再次观察到'getIndex()'方法再次被大量使用。
所有这些
方法要做的是找出段落数组的哪些部分必须包含在其中
List <BookMark>。
一个示例用法可以是:
Library lib= Library.load("/usr/jos/tmp/kj.txtlib"); // get the library
// show all text for the book Genesis:
List<Bookmark> paragraphs= lib.getParagraphs("Genesis");
for (BookMark paragraph : paragraphs)
System.out.println(paragraph);
// show all text in the entire bible"
List<BookMark> paragraphs= lib.getParagraphs();
for (BookMark paragraph: paragraphs)
System.out.println(paragraph);
结束语
上述方法使我们可以执行以下所有操作:
- 产生所有组名的列表
- 产生所有书籍名称的清单
- 产生一组书名列表
- 产生所有章节名称的列表
- 产生书中章节名称的列表
- 产生所有段落的列表
- 生成书中的段落列表
- 在书的一章中产生段落列表
这很有趣,但是仍然不允许进行高级文本检索。 即
稍微摆弄一下“ grep”,我们可以做的甚至更多。 下一章
引入查询对象,使我们能够真正地挖掘文本并产生
相当先进的结果。
本文部分再次充满了代码,我称之为一天。 我想见你
下次在本文的下一部分中。 我将附上所有的代码
到那时为止,我仍然必须为这两个圣经文本找到一个地方,所以你
可以上传它们,将它们转换为Library对象并进行播放。
现在,我希望您能稍微研究一下代码,如果发现错误或发现错误,请通知我
什么都不懂,或者只是说我是个好人;-)
我希望下周再见,
亲切的问候,
乔斯
翻译自: https://bytes.com/topic/java/insights/739705-text-retrieval-systems-4b-library
文本检索 关键词检索和