<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)  編輯  收藏 所屬分類: 基礎技術

    主站蜘蛛池模板: 在线精品一卡乱码免费| 久草视频在线免费看| 欧美在线看片A免费观看| 久久亚洲精品无码VA大香大香| 久久青草免费91线频观看不卡| 日韩亚洲人成在线综合日本| 黄页免费在线观看| 亚洲视频一区调教| 一二三四免费观看在线视频中文版 | 国产亚洲精品美女2020久久 | jizz免费一区二区三区| 免费A级毛片无码A∨男男| 久久夜色精品国产噜噜亚洲a| 四虎成人精品一区二区免费网站 | 亚洲黄色在线观看视频| 美女被cao免费看在线看网站| 久久亚洲最大成人网4438| 一级女性全黄久久生活片免费| av无码东京热亚洲男人的天堂| 精品一区二区三区高清免费观看| 亚洲视频在线免费看| 亚洲 暴爽 AV人人爽日日碰| 久久WWW免费人成人片| 欧洲亚洲综合一区二区三区 | 久久亚洲春色中文字幕久久久| 日韩精品无码区免费专区| 狼人大香伊蕉国产WWW亚洲| 亚洲人JIZZ日本人| 57PAO成人国产永久免费视频| 亚洲av中文无码乱人伦在线观看 | 免费一看一级毛片全播放| 91视频免费网站| 亚洲国产中文在线视频| 可以免费观看一级毛片黄a| 在线观看免费播放av片| 天天爽亚洲中文字幕| vvvv99日韩精品亚洲| 国产精品免费看久久久| 亚洲乱码av中文一区二区| 亚洲色WWW成人永久网址| 亚洲欧洲免费无码|