>백엔드 개발 >PHP 튜토리얼 >SCWS的scws_get_words函数是否存在bug

SCWS的scws_get_words函数是否存在bug

WBOY
WBOY원래의
2016-06-06 20:52:271097검색

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个字符的词性过滤试了一下,果然可以了。。。大家考虑下这里应该怎么改下吧

回复内容:

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);                         \
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.