<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    海闊天空

    I'm on my way!
    隨筆 - 17, 文章 - 69, 評論 - 21, 引用 - 0
    數據加載中……

    文本處理(一)狀態機(2)

    系統程序員成長計劃-文本處理(一)

    狀態機(2)

    o 用有窮狀態機解一道面試題。

    剛畢業的時候,我到一家外企面試,面試題里有這樣一道題:

    統計一篇英文文章里的單詞個數。

    有多種方法可以解這道題,這里我們選擇用有窮狀態機來解,做法如下:

    先把這篇英文文章讀入到一個緩沖區里,讓一個指針從緩沖區的頭部一直移到緩沖區的尾部,指針會處于兩種狀態:“單詞內”或“單詞外”,加上后面提到的初始狀態和接受狀態,就是有窮狀態機的狀態集。緩沖區中的字符集合就是有窮狀態機的字母表。

    如果當前狀態為“單詞內”,移到指針時,指針指向的字符是非單詞字符(如標點和空格),那狀態會從“單詞內”轉換到“單詞外”。如果當前狀態為“單 詞外”, 移到指針時,指針指向的字符是單詞字符(如字母),那狀態會從“單詞外”轉換到“單詞內”。這些轉換規則就是狀態轉換函數。

    指針指向緩沖區的頭部時是初始狀態。

    指針指向緩沖區的尾部時是接受狀態。

    每次當狀態從“單詞內”轉換到“單詞外”時,單詞計數增加一。
    這個有窮狀態機的圖形表示如下:

    下面我們看看程序怎么寫:

    int count_word(const char* text)

    {

    /*定義各種狀態,我們不關心接受狀態,這里可以不用定義。*/

    enum _State

    {

    STAT_INIT,

    STAT_IN_WORD,

    STAT_OUT_WORD,

    }state = STAT_INIT;



    int count = 0;

    const char* p = text;



    /*在一個循環中,指針從緩沖區頭移動緩沖區尾*/

    for(p = text; *p != '\0'; p++)

    {

    switch(state)

    {

    case STAT_INIT:

    {

    if(IS_WORD_CHAR(*p))

    {

    /*指針指向單詞字符,狀態轉換為單詞內*/

    state = STAT_IN_WORD;

    }

    else

    {

    /*指針指向非單詞字符,狀態轉換為單詞外*/

    state = STAT_OUT_WORD;

    }

    break;

    }

    case STAT_IN_WORD:

    {

    if(!IS_WORD_CHAR(*p))

    {

    /*指針指向非單詞字符,狀態轉換為單詞外,增加單詞計數*/

    count++;

    state = STAT_OUT_WORD;

    }

    break;

    }

    case STAT_OUT_WORD:

    {

    if(IS_WORD_CHAR(*p))

    {

    /*指針指向單詞字符,狀態轉換為單詞內*/

    state = STAT_IN_WORD;

    }

    break;

    }

    default:break;

    }

    }



    if(state == STAT_IN_WORD)

    {

    /*如果由單詞內進入接受狀態,增加單詞計數*/

    count++;

    }



    return count;

    }

    用狀態機來解這道題目,思路清晰,程序簡單,不易出錯。

    這道題目只是為了展示一些奇技淫巧,還是有一些實際用處呢?回答這個問題之前,我們先對上面的程序做點擴展,不只是統計單詞的個數,而且要分離出里面的每個單詞。

    int word_segmentation(const char* text, OnWordFunc on_word, void* ctx)

    {

    enum _State

    {

    STAT_INIT,

    STAT_IN_WORD,

    STAT_OUT_WORD,

    }state = STAT_INIT;



    int count = 0;

    char* copy_text = strdup(text);

    char* p = copy_text;

    char* word = copy_text;



    for(p = copy_text; *p != '\0'; p++)

    {

    switch(state)

    {

    case STAT_INIT:

    {

    if(IS_WORD_CHAR(*p))

    {

    word = p;

    state = STAT_IN_WORD;

    }

    break;

    }

    case STAT_IN_WORD:

    {

    if(!IS_WORD_CHAR(*p))

    {

    count++;

    *p = '\0';

    on_word(ctx, word);

    state = STAT_OUT_WORD;

    }

    break;

    }

    case STAT_OUT_WORD:

    {

    if(IS_WORD_CHAR(*p))

    {

    word = p;

    state = STAT_IN_WORD;

    }

    break;

    }

    default:break;

    }

    }



    if(state == STAT_IN_WORD)

    {

    count++;

    on_word(ctx, word);

    }



    free(copy_text);



    return count;

    }

    狀態機不變,只是在狀態轉換時,做是事情不一樣。這里從“單詞內”轉換到其它狀態時,增加單詞計數,并分離出當前的單詞。至于拿分離出的單詞來做什么,由傳入的回調函數決定,比如可以用來統計每個單詞出現的頻率。

    但如果討論還是限于英文文章,這個程序的意義仍然不大,現在來做進一步擴展。我們考慮的文本不再是英文文章,而是一些文本數據,這些數據由一些分隔符分開,我們把數據稱為token,現在我們要把這些token分離出來。

    typedef void (*OnTokenFunc)(void* ctx, int index, const char* token);



    #define IS_DELIM(c) (strchr(delims, c) != NULL)

    int parse_token(const char* text, const char* delims, OnTokenFunc on_token, void* ctx)

    {

    enum _State

    {

    STAT_INIT,

    STAT_IN,

    STAT_OUT,

    }state = STAT_INIT;



    int count = 0;

    char* copy_text = strdup(text);

    char* p = copy_text;

    char* token = copy_text;



    for(p = copy_text; *p != '\0'; p++)

    {

    switch(state)

    {

    case STAT_INIT:

    case STAT_OUT:

    {

    if(!IS_DELIM(*p))

    {

    token = p;

    state = STAT_IN;

    }

    break;

    }

    case STAT_IN:

    {

    if(IS_DELIM(*p))

    {

    *p = '\0';

    on_token(ctx, count++, token);

    state = STAT_OUT;

    }

    break;

    }

    default:break;

    }

    }



    if(state == STAT_IN)

    {

    on_token(ctx, count++, token);

    }



    on_token(ctx, -1, NULL);

    free(copy_text);



    return count;

    }

    用分隔符分隔的文本數據有很多,如:

    環境PATH,它由‘:’分開的多個路徑組成。如:
    /usr/lib/qt-3.3/bin:/usr/kerberos/bin:/backup/tools/jdk1.5.0_18/bin/:/usr/lib/ccache:/usr/local/bin:/bin:/usr/bin:/home/lixianjing/bin

    文件名,它由‘/’分開的路徑組成。如:
    /usr/lib/qt-3.3/bin

    URL中的參數,它‘&’分開的多個key/value對組成。
    hl=zh-CN&q=limodev&btnG=Google+搜索&meta=&aq=f&oq=

    所有這些數據都可以用上面的函數處理,所以這個小函數是頗具實用價值的。

    posted on 2009-07-10 21:16 石頭@ 閱讀(323) 評論(0)  編輯  收藏 所屬分類: 基礎技術

    主站蜘蛛池模板: 亚洲人成电影网站免费| 久久精品无码专区免费| 精品免费久久久久久久| 亚洲成人在线网站| 国产猛男猛女超爽免费视频| 亚洲线精品一区二区三区影音先锋| 永久免费观看黄网站| 免费人成视频在线观看不卡| 美女被吸屁股免费网站| 亚洲国产精品人人做人人爱| 国产精品亚洲专一区二区三区| 国产一级理论免费版| 一本到卡二卡三卡免费高| 国产日产亚洲系列| 中文字幕视频在线免费观看| 亚洲av女电影网| 国产精品色拉拉免费看| 亚洲综合av一区二区三区| 国产片免费福利片永久| 国产美女视频免费观看的网站| 亚洲中文久久精品无码| 99久热只有精品视频免费观看17| 亚洲免费在线观看视频| 国产精品成人无码免费| 韩国免费A级毛片久久| 久久亚洲精品无码aⅴ大香| 毛片a级毛片免费观看品善网| 黄页视频在线观看免费| 亚洲AV第一页国产精品| 日韩一区二区a片免费观看| 91福利免费体验区观看区| 亚洲AV无码成人专区| 免费少妇a级毛片人成网| 少妇性饥渴无码A区免费| 亚洲资源最新版在线观看| 免费成人午夜视频| 一级做a爰全过程免费视频| 亚洲国产精品网站在线播放| 亚洲色欲久久久久综合网| 亚洲精品在线免费看| 四虎成人精品国产永久免费无码|