首页 > 开发 > php > 正文

SCWS的scws_get_words函数是否存在bug

2017-09-06 15:07:34  来源:网友分享

SCWS是国人做的一个很优秀的分词库,它的php扩展可以方便地处理中文分词。现在发现其中一个函数scws_get_words函数的问题,这个函数是用来获取分词结果的,其第二个参数可以指定你需要返回的结果,这是它的c api文档描述(php大同小异)

·scws_top_t scws_get_words(scws_t s, char *xattr);
描述:返回指定词性的关键词表,系统会根据词语出现的先后插入列表。参数 xattr 用来描述要排除
或参与的统计词汇词性,多个词性之间用逗号隔开。当以~开头时表示统计结果中不包含这些词性,
否则表示必须包含,传入 NULL 表示统计全部词性。
返回值:返回词表集链表的头指针,该词表集必须调用 scws_free_tops 释放
错误:无

也就是说我只需要在第二个参数里加上以逗号分割的参数即可,比如我加上'~Ag,~a,~ad,~b,~c,~Dg,~d,~e'字符,表示我要在结果中过滤掉这些。

但实际的结果是,无论你加了多少过滤条件都不起作用,但相反,如果你只加一个过滤条件比如'~a',也就是没有逗号的时候,它可以把相应的结果过滤掉。所以我考虑这之中是否存在bug。下面附上此函数的c实现代码,大家帮我看看

// get words by attr (rand order)scws_top_t scws_get_words(scws_t s, char *xattr){	int off, cnt, xmode = SCWS_NA;	xtree_t xt;		scws_res_t res, cur;	scws_top_t top, tail, base;	char *word;	word_attr *at = NULL;	if (!s || !s->txt || !(xt = xtree_new(0,1)))		return NULL;	__PARSE_XATTR__;	// save the offset.	off = s->off;	s->off = 0;	base = tail = NULL;	while ((cur = res = scws_get_result(s)) != NULL)	{		do		{			/* check attribute filter */			if (at != NULL)			{				if ((xmode == SCWS_NA) && !_attr_belong(cur->attr, at))					continue;				if ((xmode == SCWS_YEA) && _attr_belong(cur->attr, at))					continue;			}			/* put to the stats */			if (!(top = xtree_nget(xt, s->txt + cur->off, cur->len, NULL)))			{				top = (scws_top_t) malloc(sizeof(struct scws_topword));				top->weight = cur->idf;				top->times = 1;				top->next = NULL;				top->word = (char *)_mem_ndup(s->txt + cur->off, cur->len);				strncpy(top->attr, cur->attr, 2);				// add to the chain				if (tail == NULL)					base = tail = top;				else				{					tail->next = top;					tail = top;				}				xtree_nput(xt, top, sizeof(struct scws_topword), s->txt + cur->off, cur->len);			}			else			{				top->weight += cur->idf;				top->times++;			}		}		while ((cur = cur->next) != NULL);		scws_free_result(res);	}	// free at & xtree	if (at != NULL)		free(at);	xtree_free(xt);	// restore the offset	s->off = off;	return base;}

我发现它的__PARSE_XATTR__宏有些问题啊,这里另外附上word_attr的结构定义

/* macro to parse xattr -> xmode, at */#define	__PARSE_XATTR__		do {				\	if (xattr == NULL) break;				\	if (*xattr == '~') { xattr++; xmode = SCWS_YEA; }	\	if (*xattr == '\0') break;				\	cnt = ((strlen(xattr)/2) + 2) * sizeof(word_attr);	\	at = (word_attr *) malloc(cnt);				\	memset(at, 0, cnt);					\	cnt = 0;						\	for (cnt = 0; (word = strchr(xattr, ',')); cnt++) {	\		strncpy(at[cnt], xattr, 2);			\		xattr = word + 1;				\	}							\	strncpy(at[cnt], xattr, 2);				\} while (0)typedef char word_attr[4];

这样处理xattr的话,只能处理词性是2个字符的情况,因为它strncpy(at[cnt], xattr, 2);。这也太马虎了吧,词性表里有一堆一个字符的词性啊,它copy的话就会把逗号也copy进去啊。

自己全部用2个字符的词性过滤试了一下,果然可以了。。。大家考虑下这里应该怎么改下吧

解决方案

跟作者交流了下,hightman给出了patch,修改宏定义处

diff -c -r1.28 -r1.29*** libscws/scws.c  5 Aug 2011 04:39:33 -0000   1.28--- libscws/scws.c  26 Oct 2011 08:41:44 -0000  1.29****************** 1278,1284 ****    memset(at, 0, cnt);                                 \    cnt = 0;                                            \    for (cnt = 0; (word = strchr(xattr, ',')); cnt++) { \!       strncpy(at[cnt], xattr, 2);                     \        xattr = word + 1;                               \    }                                                   \    strncpy(at[cnt], xattr, 2);                         \--- 1278,1285 ----    memset(at, 0, cnt);                                 \    cnt = 0;                                            \    for (cnt = 0; (word = strchr(xattr, ',')); cnt++) { \!       at[cnt][0] = *xattr++;                          \!       at[cnt][1] = xattr == word ? '\0' : *xattr;     \        xattr = word + 1;                               \    }                                                   \    strncpy(at[cnt], xattr, 2);                         \