SCWS的scws_get_words函数是否存在bug


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 c php 中文分词

永远的HIME 11 years, 8 months ago

跟作者交流了下,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);                         \
天道CHEN answered 11 years, 8 months ago

Your Answer