一、简介
Xapian与开源
Xapian的官方网站是,这是一个非常优秀的开源搜刮引擎项目,搜刮引擎着实只是一个普通的说法,正式的说法着实是IR(Information Retrieval)体系。Xapian的License是GPL,这意味着允许应用者自由地批改其源码并公布之。Xapian的中文原料非常少,可以说如今互联网上连一篇完备具体的Xapian中文先容文档,更别说中文API文档了。着实,Xapian的英文原料也不多,除了官方网站上的Docs和Wiki外,尚有一些网站上的邮件列表,在这方面跟Lucene没得比。固然,Lucene如今已经生长到2.x版本了,而Xapian的最新版本才1.012,海外开源项目一样平常对版本号独霸得比拟严肃,一个项目一样平常到了1.x才算稳定和成熟的。
Xapian可以运行在那些平台?
Xapian由C++编写,但可以绑定到Perl, Python, PHP, Java, Tcl, C# 和Ruby乃至更多的语言,Xapian可以说是STL编程的典范,在这里您可以找到熟识的引用计数型智能指针、容器和迭代器,乃至连定名也跟STL雷同,信赖肯定能引起喜好C++和STL的你的共识(实际上,很少C++措施员完备不应用STL)。由于Xapian应用的是STL和C运行时库,因此具有高度可移值性,官方说法是可以运行在Linux、 Mac OS X、 FreeBSD、 NetBSD、 OpenBSD、Solaris,、HP-UX,、Tru64和IRIX,,乃至其余的Unix平台,在Microsoft Windows上也跑得很好。固然,并不能像Java那样“一次编译,到处可以运行”,当移植到其余平台时,一样平常来说是必要重新编译的。至于如安在Windows32位体系下编译Xapian,请查阅我早年写的文章《nmake在windows平台下编译xapian》。
Xapian的特性
依官方的说法,Xapian是一个允许开拓职员轻易地添加高等索引和搜刮功能到他们的操纵体系的高度可批改的器材,它在支持概率论检索模子的同时也支持布尔型操纵查询集。
从功能特性上来说。Xapian和Lucene有点雷同,两者都具有Term、Value(在Lucene里称为SortField)、Posting、Position和Document,不外Xapian没有Field的观念,这直接导致Xapian在应用上比Lucene贫穷了那么一点。但这完备不是题目,通过一些小技能,完备可以本身在Xapian中实现Filed的观念。在Lucene里尚有一个叫Payload的元素,即词条 (Term) 的元数据或称载荷。举一个例子,“回家用饭吧”和“快回家用饭”这两个句子都带有“用饭”这个词语,但在检索的时间怎样手法将语气表达出来呢?固然可以添加Term来办理这个题目,但由于Term的索引信息和存储信息是分开放的,相对来说I/O性能较差,Payload就是应这个题目而生的,由于Payload信息是直接放在索引里的。由于对Xapian的研讨还不是很深,Xapian里是否有类似Payload这个观念,还必要连续研讨。
Xapian与搜刮
搜刮的方针是将结果数据揭示给终端用户,搜刮引擎与平凡的数据库查询最大的不同就在于查询。Xapian供给了多种的查询机制。
概率性搜刮排名 – 重要的词语会比不那么重要的词语得到更多的权重,因此与权重高的词语关联的Documents会排到结果列表的更前面。
干系度反馈 – 通过赐与一个或多个Documents, Xapian可以表现最干系的Terms以便扩张一个Query,及表现最干系的Documents。
词组和左近搜刮 -- 用户可以搜刮一个精确短语或指定命组的词组。
全方位的布尔型搜刮器,譬喻 ("stock NOT market", etc)。
支持提取搜刮关键字的词干,譬喻当搜刮“football”的时间,当Documents中含有"footballs" 或"footballer"的时间也被认作切合。这有助于找到干系结果,不然也许错过之。词干提取器如今支持Danish、Dutch、 English、 Finnish、 French、 German、 Hungarian、Italian、 Norwegian、Portuguese、Romanian、 Russian、Spanish、Swedish和Turkish。
支持通配符查询,譬喻“xap*”。
支持别名查询,打个比喻,C++会主动转为CPlusPlus,C#则主动转为CSharp。
Xapian支持拼写更正,譬喻xapian会被更正为xapain,固然这必需基于词组已经被索引了。这特性跟Google供给的“你是不是想搜刮xxx”有点雷同。
Xapian的存储体系
Xapian如今的版本默认是应用flint作为存储体系,flint是以块的形式来存储,默认每块是8K,理论上每一个文件最大可以到达2048GB。固然,在旧式的文件体系,譬喻FAT/FAT32是不也许实现的。熟识Windows内存管理机制的朋侪肯定知道应用Windows32位体系每个进程的总假造所在空间只有4GB,而用户模式连2GB都不足(Windows2003可以将用户模式扩张到3GB左右),因词攀??用措施不也许一次过将所有Database文件读取到内存中,通常的做法是应用内存映射文件,先预订所在空间,在真正应用的时间才调拨内存,而内存分页粒度是4k,也就是说内存中每一页是4k,而在IA64体系中,内存分页粒度是8k。在内存中,除了页外,尚有区块,X86和IA64的内存区块的粒度都是64k。Xapian如许存储数据预计是为了在各个平台上都能实现数据对齐,数据对齐对付cpu运算寻址黑白常重要的,而8和64都是4的倍数,因此大胆猜想Xapian以8k作为存储体系的默认块巨细是为了在性能和兼容性中取得最均衡和最优值。
Xapian应用unsigned 32-bit ints作为Documents的id值,因此在每个Xapian的Database中,最多可容纳40亿个Documents。而Xapian的Terms和Documents都是应用B-树来存储的,着实很多数据库体系(这里所指的是相干数据库)的索引都是用B-树或B+树来存储的,具有增编削查比拟利便敏捷的特性,错误则是假如索引被删除后的空间不能频频操作,为了前进性能,通常要常常重修索引。
Xapian的性能
搜刮引擎的性能是用户非常关怀的一部分,Xapian的性能怎样?官方的原话如下:The short answer is "very well" - a previous version of the software powered BrightStation's Webtop search engine, which offered a search over around 500 million web pages (around 1.5 terabytes of database files). Searches took less than a second.。在5亿个网页共1.5TB巨细的文件中,搜刮只必要小于一秒就完事了。固然,这跟运行的平台和呆板是亲近干系,在我们本身构建好Xapian搜刮引擎操纵后,我们也可以测测具体的速率。
Xapian的绝佳典型
Xapian的官方网站上有一个绝佳的应用典型,这个称为Omega的项目乃至可以开箱即用作为一个CGI操纵措施。Omega附带了Omindex和ScriptIndex这两个索引天生器材,可以将硬盘上的html,pdf,图片乃至视频影片索引起来并天生Database,通过操纵这些由Omindex或ScriptIndex天生的Database,Omega供给了搜刮这些文件的功能。
关于《操作Xapian构建本身的搜刮引擎》系列
在应用Xapian的过程中,我一样平常是查阅上的Doc、API Doc和Wiki,碰到费力时则查阅Omega的源代码并相互印证之。着实没办法的时间只能从Google上找找一些网站的邮件列表,可以说是磕磕碰碰地将Xapian的大部分功能玩了一遍。有一些专着名词我固然知道大概意思,但无法精确地翻译出来,因此《操作Xapian构建本身的搜刮引擎》这一系列的内容也许会错漏百出。不外假如这一系列文章可以引起人人对Xapian的乐趣,它所得到的批驳才是它最大的代价。
在后续文章中,我会从Xapian的Database起头一步一步构建搜刮引擎操纵,并配以本身的相识,请人人一起讨论。
由于事变缘故起因,一样平常只有晚上才有工夫写文章,在写的过程中还要不绝印证本身的想法是否精确,省得常常作无谓揣摩而导致短缺严谨性,因此不能担保每天都能更新,请人人留情。
二、Database
在Xapian1.0之前,是应用quartz作为database文件技俩的,不外自从1.0之后,便改用Flint作为database的文件技俩了。偶然间,我们会将database称为“索引”,在Xapian中,索引通常比被索引的documents还要多,这表示Xapian做一个信息检索体系比做一个信息存储体系更适宜。
Database的存储结构
Xapian的database是全部用于检索的信息表的聚拢,以下的表是必需的:
l posting list table 生涯了被每一个term索引的document,实际上生涯的应当是document在database中的Id,此Id是唯一的。
l record table 生涯了每一个document所关联的data,data不能通过query检索,只能通过document来获取。
l term list table 生涯了索引每个document的全部的term。
以下的表是可选的,即当有以下的范例的数据必要被存储的时间才会出现(在1.0.1早年,position和value表就算是没有数据的时间也会被创建,而spelling和synonym表是1.0.2后才出现的)。
l position list table 生涯了每一个Term出如今每一个document中的职位
l value table 生涯了每一个document的values,values是用作生涯、排序或其余作用的。
l spelling table 生涯了拼写更正的数据。
l synonym table 生涯术语的字典,譬喻NBA、C#或C++等。
以上的每一个聚拢是生涯在独立的文件中,以便允许管理员查察此中的数据。方才说了,有一些表不是必需的,譬喻当您不必要词组搜刮的时间,没须要存储任何的postionlist信息。
假如你看过Xapian的database,你会创造以上的每一个表着实是应用了2到3个文件的,假如您正在应用“flint”作为database的存储技俩,那么termlist表会被存储为以下三个文件“termlist.baseA”、“termlist.baseB”、“termlist.dB”。在这些文件中,着实只有”.db”文件存储了真实的数据,“.baseA”和“baseB”文件是用作跟踪假如于“.dB”文件中查找数据。通常只会出现一个“.baseA”文件和一个“.baseB”文件。
在前一篇《操作Xapian构建本身的搜刮引擎:简介》中提到过,Xapian如今的版本默认是应用flint作为存储体系,“.dB”文件是以块的形式来存储,默认每块是8K,第一块是用作信息头,假如应用UltraEdit等二进制查察器材,会创造全部“.dB”文件的前三个字节都是0x00。因此,当“.dB”中仅有一条数据的时间所有文件也会有16KB时,切莫大惊小怪。
变化“.dB”文件的默认块巨细会导致性能变动,但结果很难说是好是坏,由于这是跟所承载的硬件平台与操纵体系平台有关的。一样平常来说,B树的分支因子(即每个结点能容纳的关键字的数量)越大,B树的查找性能就越强;但由于通常情况下,B树的结点都是存储在永世存储体系(譬喻硬盘/磁带)中,每次拜访某个结点都市将所有结点由永世存储体系读入到内存中,这是一个博弈的过程:假设一棵数据量很大的B树,将B树的分支因子设到很大,这棵B树会长得很矮,从理论上来说查找性能也许很高。但如许就带来了一个错误,每个结点所占的内存非常多,假如在一个并发拜访量很大的IR体系中采用这种行动的话所应用的内存肯定黑白常可观的。因此在调度“.dB”文件的默认块巨细的时间肯定要充沛思量cpu体系和操纵体系平台,以便调度到最佳性能。
原子性批改
Xapian能担保对database的全部批改都是原子性的,这意味着:
l 从一个独立的进程(或一个独立的database器材在同一个进程)角度来看,在读取数据库的时间,直到批改乐成提交,全部对数据库的批改都是不可见的。
l Database在硬盘中的状况始终是维持同等的。
l 假如在批改的过程中体系发生间断,只要硬件不发生妨碍(硬盘粉碎),就算电源被堵截,database应当总是被还原到有效的状况。
提交一个批改必要屡次的体系调用,以便使全部缓存的批改能革新到硬盘上,如许能确保就算体系在任何时间发生过错,database也能处于同等的状况。固然,如许相对付说会慢了一点(由于体系已经预备好往硬盘上写数据了),因此将屡次的批改组合在一起往硬盘上写会有肯定的性能晋升。
多个批改操纵可以显式地组合在一个事宜中,假如一个操纵措施不显式地应用事宜来掩护批改操纵,Xapian会将这些批改组合在一个事宜中,然后批量举办批改。请留意,Xapian如今临时还不能跨database举办事宜操纵。
假如要想敏捷地天生非常大的database,请应用“DANGEROUS”关键字搜刮Xapian的邮件列表,其供给了可以重新编译Xapian而不采用原子性批改的行动,这功能已经不再整合在xapian的标准版本中了。
Single writer, multiple reader
Xapian实现了“单写多读”的模式,这意味着任何时间,同一时候只允许一个器材可以批改database,但允许多个器材可以读取database。
在*nix体系下,Xapian应用“lock-files”逼迫约束来实现此模式,在一个flint database中,每一个Xapian的database目次包孕了一个名为“flintlock”的文件以作锁定用场。此文件总在存在于database的目次里,当database被打开用作写入的时间,此文件会被fcntl()行动锁定。每一个WritableDatabase打开的时间,都市发生一个子进程以便举办锁定操纵。假如某个database写入器(一样平常是指WritableDatabase)还没有机会推行开释锁的消除操纵便退出了(譬喻这个WritableDatabase地点的操纵程被杀逝世了),fcntl()发生的锁会主动被操纵体系开释。
在Microsoft Windows下,应用的是另一种锁定的能力,此能力不必要发生了子进程来举办锁定操纵,但同理,当写入器退出时,操纵体系依然会主动开释锁。熟识Windows机制的朋侪知道,Windows是应用文件句柄来操纵文件的,而文件句柄是属于内核资源的一种,在任何情况下,Windows都能担保操纵措施在退出时能开释全部的资源。
网络文件体系
Xapian如今可以事变在一个网络文件体系中,但存在着大宗的隐蔽题目。因此提议在陈设前要大宗地在特定的网络中测试。
请留意,Xapian黑白常凭借I/O操纵的,除非处于一本性能非常优秀的网络,在一个网络文件体系中举办操纵会相对的慢一点。
Xapian必要可以在database的目次里创建一个lock file,在某些网络文件体系中(譬喻NFS),这必要一个锁定的守卫进程在运行,也就是上面所提到的子进程。
创建database
说了一堆的理论,下面我们来实战创建一个database。Xapian里的全部类都处于Xapian这个定名空间里,Xapian::Database是全部Database的基类,实际上,它只有一个子类,那就是WritableDatabase。从面向器材的角度来看,这两个类计划得非常好,Xapian::Database拥有大部分只读或内存操纵的行动,而Xapian::WritableDatabase则拥有事宜操纵,革新数据到硬盘等行动。
有几种创建Database的行动:
l Flint 假如你是应用长途后端(指网络文件体系),请应用Xapian::Flint::open()行动来创建database。应用此行动你能得到更多的独霸,譬喻创建只读的database,或创建可写的database。
l Auto auto并不是一种database技俩,你可以创建一个“database存根”文件,此文件能列出一到多个database的路径,这些路径可以作为Xapian::Database的布局函数的参数,从而被主动检测是哪种范例的database。尚有,假如将一个文件路径名称而非目次名称作为参数传入到Xapian::Database的布局函数中,Xapian::Database会认为你传入了一个“database存根”文件;固然,你也可以应用Xapian::Auto::open_stub()来显式打开一个存根文件。上面说的也许有点绕口,“database存根文件”的技俩是每个database一行,譬喻:
remote localhost:23876
flint /var/spool/xapian/webindex
这下该明白了。
l Inmemory 还可以创建内存databse,这种范例的database是生涯在内存中。请留意,通过Xapian::InMemory::open()返回的范例是WritableDatabase,这意味着这是一个可革新到硬盘上的database,它最初是为测试之用,但在成立临时的小数据库也也许是有效的。
实际上,创建一个database尚有更通用的行动,譬喻通过将一个database地点的完备目次作为一个字符串传入到Xapian::Database的布局函数中实例化一个器材后,即可得到一个只读的database。而Xapian::WritableDatabase则繁杂一点,除了要传入database的路径外,还必要设定怎样打开database。有以下几个参数:
l Xapian::DB_CREATE_OR_OPEN 打开以便读写,假如不存在则创建。
l Xapian::DB_CREATE 总是创建新的database,假如存在则失败。
l Xapian::DB_CREATE_OR_OVERWRITE 假如database存在的话则覆盖之,假如不存在则创建。
l Xapian::DB_OPEN 打开以便读写,假如不存在则失败。
乐成打开一个datababse是Xapian全部后续操纵如检索,写入的根本。你乃至可以将通过add_database()行动则多个database组合在一起拜访;假如想将database革新到硬盘中,则推行flush()行动则可。末了,假如不想应用database了,将database器材销毁即可。
小结
在这一章里,好像并没有几何具体的操纵,但database是Xapian的存储体系,在Xapian全部操纵的根本,只有明白明白了Xapian的存储行动手法更好更高效地布局本身的搜刮引擎。同时,假如您之前并没有对大型的文件存储体系有所了解的话,这篇文件可以多几何少带给您一些启示。在下一章里,我会连续先容Document和Term等Xapian的构成部分。
三、Document、Term和Value
在上一篇《操作Xapian构建本身的搜刮引擎:Database》里指出database是Xapian的根本,而这一篇里讲到的documents、terms和values则是索引和查询的须要构成部分。
Documents 、terms and posting
在信息检索(IR)中,我们规划要获取的项称之为“document”,每一个document是被一个terms聚拢所刻画的。 “document”和“term”这两个词汇是IR中的术语,它们是来自“图书馆管理学”的。通常一个document认为是一块文本,. Usually a document is thought of as a piece of text, most likely in a machine readable form, 而一个term则是一个词语或短语以用作刻画document的,在document中大多数会存在着多个term,譬喻某个document是跟口腔卫生干系的,那么也许会存在着以下的terms:“tooth”、“teeth”、“toothbrush”、“decay”、 “cavity”、“plaque”或“diet”等等。
假如在一个IR体系中,存在一个名为D的document,此document被一个名为t的term所刻画,那么t被认为索引了D,可以用以下式子表示:t->D。在实际操纵的一个IR体系中通常是多个documents,如D1, D2, D3 ...构成的聚拢,且有多个term,如t1, t2, t3 ...构成的聚拢,从而有以下相干:ti -> Dj。
假如某个特定的term索引了某个特定的document,那么称之为posting,说白了posting就是带position信息的term,在干系度检索中也许有肯定的用场的。
给定一个名为D的document,存在着一个terms列表索引着它,我们称之为D的term list。
给定一个名为t的term,它索引着一个documents列表,这称之为t的posting list(应用“Document list”也许会在叫法上更同等,但听起来过于朴陋)。
在一个存在于打定机的IR体系中,terms是存储于索引文件中的。term可以用作有效地查找它的posting list,在posting list里,每一个document带有一个很短的标识符,就是document id。大略来说,一个posting list可以被认为是一个由document ids构成的聚拢,而term list则是一个字符串构成的聚拢。在某些IR体系的内部是应用数字来表示term的,因此在这些体系中,term list则是数字构成的聚拢,而Xapian则不是如许,它应用原汁原味的term,而应用前缀来紧缩存储空间。
Terms不肯定是要是document中出现的词语,通常它们会被转换为小写,并且每每它们被词干提取算法处理赏罚过,因此通过一个值为“connect”的term也许会检索出一系列的词语,譬喻“connect”、“connects”、“connection”或“connected”等,而一个词语也也许发生多个的terms,譬喻你会将提取出的词干和未提取的词语都索引起来。固然,这也许只适用于英语、法语或拉丁语等西欧系列的语言,而中文的分词则有很大的不同,总的来说,西欧语系的语言分词与中文分词有以下的不同:
l 拿英语来说,通常情况下英语的每一个词语之间是用空格来离隔的,而中文则不然,乃至可以极度到整篇文章都不出现空格或标点标记。
l 像上面提到的,“connect”、“connects”、“connection”或“connected”判别的意思“动词性子的毗连”、“动词性子的第三人称的毗连”、“名称性子的毗连”或“毗连的已往式”,但在中文里,用“毗连”就可以表示整个了,险些不必要词干提取。这意味着英语的种种词性大部分是有章可循的,而中文的词性则是天马行空的。
l 第二点只是中文分词非常费力的一个缩影,要完备精确地标识出某个句子的语意是很费力的,譬喻“中华黎民共和国创立了”这个句子,可以分出“中华”、“华人”、“黎民”、“共和国”、“创立”等词语,不外此中“华人”跟这个句子着实相干不大。咋一眼看上去很大略,但呆板那有这么轻易懂这此中的微妙呢?
Values
Values是附加在document上一种元数据,每一个document可以有多个values,这些values通过差别的数字来标识。Values被计划成在匹配过程中快速地拜访,它们可以用作排序、列队过剩频频的document和范畴检索等用场。固然values并没有长度限定,但最好让它们尽也许短,假如你仅仅是想存储某个字段以便作为结果表现,那么提议您最好将它们生涯在document的data中。
Document data
每一个Document只有一个data,可以是恣意范例技俩的数据,固然在存储的时间请先转换为字符串。这听上去也许有点古怪,原形是如许的:假如要存储的数据是文本技俩,则可以直接存储;假如要存储的数据是种种的器材,请先序列化成二进制流再生涯,而在读取的时间反序列化读取。
UTF-8与Unicode
Xapian里的全部对象是用UTF-8来生涯的,UTF-8是Unicode的一种实现。如今很多人用VC为了利便是将编码设成“未设置”或“多字节”的,也就是说用的是体系内码(GB2312/GBK/ GB18030),如许的话则将数据生涯到Xapian前要先转码为UTF-8,而从Xapian里读出的数据则要转码为GB2312/GBK/ GB18030手法精确表现,这里推举用iconv,这是一个非常利便的库。
分词
很多文章都说如今的中文分词已经很成熟的,但据实际视察,google或百度等大公司的分词引擎都是本身开拓或有专门的公司开拓的,简直已经算比拟成熟。但市场上供给免费乃至开源的分词引擎不多,中科院研发的ictclas30分词精确度和分词速率都非常不错,并且尚有词性标注和自界说添加词的功能,可惜不开源。其它比拟受迎接的尚有libmmseg和SCWS,因此都是开源的,不外经测试libmmseg的分词精度好像不高,而SCWS由于应用了大宗的递归,在天生词库的时间常常导致栈溢出(我是用vc2005编译的),必要本身将递归批改为循环,从演示的情况来看,SCWS的分词精度来算可以。
实战
由于Xapian并不像Lucene那样有Field的观念,因此一样平常采用以大写字母作为Term和posting的前缀,但单个字母的前缀对措施员太不交情了,以是一样平常的做法是自界说一个用户前缀到term前缀的映射,如Title=>T,而Xapian的QueryParser也支持这种映射,QueryParser是查询表明器,能将一段字符串表明为Xapian的Query,后头会延续提到。
添加document的例子:
Xapian::Document doc;
doc.add_term("K你好");
doc.add_term("K那边");
//posting是带position的term
doc.add_posting("K用饭", 14);
doc.add_posting("K玩耍", 8);
/*
这里最好先用一个map
这里应用起来像Lucene的SortField一样了。
*/
doc.add_value(1, "1");
doc.set_data("你好啊,在那边玩耍呢?还没用饭吗?");
//创建一个可写的db
Xapian::WritableDatabase db("c:\\db");
//将document介入到db中,返回document的id,此id在db中是唯一的
Xapian::docid id = db.add_document(doc);
//革新到硬盘中
db.flush();
获取document信息的例子:
//获取
Xapian::Document doc = db.get_document(id);
string v = doc.get_value(1);
printf(v);//输出
string data = doc.get_data();
printf(data);//输出"你好啊,在那边玩耍呢?还没用饭吗?"
for (Xapian::TermIterator iter = doc.termlist_begin(); iter != doc.termlist_end(); ++iter)
{
printf(*iter);//依次输出term和posting
}
上面的两个例子比拟大略,假如要想更深入请查阅Omega的代码,内里有更繁杂的操纵。值得一提的Xapian里有一个TermGenerator,可以更利便地索引数据,不外这个类有两个不知道算不算错误的特性:起首是凭借Stem,对付中文来说除非本身实现了一个Stem,不然TermGenerator用场不大;其它TermGenerator会主动将天生的term或posting添加“Z”前缀。
在这里要提一下一个名为“Xapwrap”的东东,这是某个外国人用python写的一个封装Xapian的类库,内里某些头脑照样不错的,只可惜只兼容Xapian 1.x之前的版本。我本身封装的类有一部分就是参考Xapwrap的。
下面是一段我正在用的代码:
//CXapianDocument是封装过的Xapian::Document
void doSegment(CXapianDocument& document, const char* lpszInput, string strUserPrefix)
{
//先分词,这里应用的是中科院的分词引擎
int nCount = ICTCLAS_GetParagraphProcessAWordCount(lpszInput);
result_t *result =(result_t*)malloc(sizeof(result_t)*nCount);
//获取分词结果
ICTCLAS_ParagraphProcessAW(nCount,result);
string termPrefix;
//通过用户前缀取得term前缀,这是我自界说的一个宏
GetTermPrefixFromMap(this->m_userPrefixToTermPrefixMap, strUserPrefix, termPrefix)
for (int i=0; i { //漠视标点标记,标点标记的词性标注为w开头的 if(result[i].sPOS[0] == 'w') { continue; } char buf[100]; memset(buf, 0, 100); int index = result[i].start; memcpy(buf,(void *)(lpszInput+index), result[i].length); //添加posting document.AppendPosting(termPrefix, buf, result[i].start); }
free(result);
}