<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
    數據加載中……

    文本處理(二)


     

    Builder模式

    前面我們學習了狀態機,并利用它來解析各種格式的文本數據。解析過程把線性的文本數據轉換成一些基本的邏輯單元,但這通常只是任務的一部分,接下來我們還要對這些解析出來的數據進一步處理。對于特定格式的文本數據,它的解析過程是一樣的,但是對解析出來的數據的處理卻是多種多樣的。為了讓解析過程能被重用,就需要把數據的解析和數據的處理分開。

    現在我們回過頭來看一下前面寫的函數parse_token,這個函數把用分隔符分隔的文本數據,分離出一個一個的token。

    parse_token的函數原型如下:

    typedef void (*OnTokenFunc)(void* ctx, int index, const char* token);
    int parse_token(const char* text, const char* delims, OnTokenFunc on_token, void* ctx)

    parse_token負責解析數據,但它并不關心數據代表的意義及用途。對數據的進一步處理由調用者提供的回調函數來完成,函數 parse_token每解析到一個token,就調用這個回調函數。parse_token負責數據的解析,回調函數負責數據的處理,這樣一來,數據的解析和數據的處理就分開了。

    parse_token可以認為是Builder模式最樸素的應用。現在我們看看Builder 模式:

    Builder 模式的意圖:將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。“構建”其實就是前面的解析過程,而“表示”就是前面說的對數據的處理。

    對象關系:
    對象關系
    上面的parse_token與這里的Director對應。

    上面的回調函數與這里的Builder對應。

    具體的回調函數與這里的ConcreteBuilder對應。

    對數據處理的結果就是Product。

    對象協作:
    對象協作
    Client是parse_token的調用者。

    由于parse_token是按面向過程的方式設計的,所以ConcreteBuilder和Director的創建只是對應于一些初始化代碼。

    調用parse_token相當于調用aDirector的Construct函數。

    調用回調函數相當于調用aConcreteBuilder的BuildPart函數。

    回調函數可能把處理結果存在它的參數ctx中,GetResult是從里面獲取結果,這是可選的過程,依賴于具體回調函數所做的工作。

    parse_token的例子簡單直接,對于理解Builder模式有較大的幫助,不過畢竟它是面向過程的。現在我們以前面的XML解析器為例來說明Builder模式,雖然我們的代碼是用C寫的,但完全是用面向對象的思想來設計的。Builder是一個接口,我們先把它定義出來:

    struct _XmlBuilder;
    typedef struct _XmlBuilder XmlBuilder;

    typedef void (*XmlBuilderOnStartElementFunc)(XmlBuilder* thiz, const char* tag, const char** attrs);
    typedef void (*XmlBuilderOnEndElementFunc)(XmlBuilder* thiz, const char* tag);
    typedef void (*XmlBuilderOnTextFunc)(XmlBuilder* thiz, const char* text, size_t length);
    typedef void (*XmlBuilderOnCommentFunc)(XmlBuilder* thiz, const char* text, size_t length);
    typedef void (*XmlBuilderOnPiElementFunc)(XmlBuilder* thiz, const char* tag, const char** attrs);
    typedef void (*XmlBuilderOnErrorFunc)(XmlBuilder* thiz, int line, int row, const char* message);
    typedef void (*XmlBuilderDestroyFunc)(XmlBuilder* thiz);

    struct _XmlBuilder
    {
    XmlBuilderOnStartElementFunc on_start_element;
    XmlBuilderOnEndElementFunc on_end_element;
    XmlBuilderOnTextFunc on_text;
    XmlBuilderOnCommentFunc on_comment;
    XmlBuilderOnPiElementFunc on_pi_element;
    XmlBuilderOnErrorFunc on_error;
    XmlBuilderDestroyFunc destroy;

    char priv[1];
    };

    static inline void xml_builder_on_start_element(XmlBuilder* thiz, const char* tag, const char** attrs)

    {

    return_if_fail(thiz != NULL && thiz->on_start_element != NULL);

    thiz->on_start_element(thiz, tag, attrs);

    return;

    }

    static inline void xml_builder_on_end_element(XmlBuilder* thiz, const char* tag)

    {

    return_if_fail(thiz != NULL && thiz->on_end_element != NULL);

    thiz->on_end_element(thiz, tag);

    return;

    }

    ...
    (其它inline函數不列在這里了)

    XmlBuilder接口要求實現下列函數:

    on_start_element:解析器解析到一個起始TAG時調用它。
    on_end_element:解析器解析到一個結束TAG時調用它。
    on_text:解析器解析到一段文本時調用它。
    on_comment:解析器解析到一個注釋時調用它。
    on_pi_element:解析器解析到一個處理指令時調用它。
    on_error:解析器遇到錯誤時調用它。
    destroy:用銷毀Builder對象。

    on_start_element和on_end_element等函數相當于Builder模式中的BuildPartX函數。

    XML解析器相當于Director,在前面我們已經寫好了,不過它對解析出來的數據沒有做任何處理。現在我們對它做些修改,讓它調用XmlBuilder的函數。

    XML解析器對外提供下面幾個函數:

    o 構造函數。

    XmlParser* xml_parser_create(void);

    o 為xmlParser設置builder對象。

    void       xml_parser_set_builder(XmlParser* thiz, XmlBuilder* builder);

    o 解析XML

    void       xml_parser_parse(XmlParser* thiz, const char* xml);

    o 析構函數

    void       xml_parser_destroy(XmlParser* thiz);

    在解析時,解析到相應的tag,就調用XmlBuilder相應的函數:

    o 解析到起始tag時調用xml_builder_on_start_element

    static void xml_parser_parse_start_tag(XmlParser* thiz)
    {
    enum _State
    {
    STAT_NAME,
    STAT_ATTR,
    STAT_END,
    }state = STAT_NAME;

    char* tag_name = NULL;
    const char* start = thiz->read_ptr - 1;

    for(; *thiz->read_ptr != '\0'; thiz->read_ptr++)
    {
    char c = *thiz->read_ptr;

    switch(state)
    {
    case STAT_NAME:
    {
    if(isspace(c) || c == '>' || c == '/')
    {
    tag_name = (char*)xml_parser_strdup(thiz, start, thiz->read_ptr - start);
    state = (c != '>' && c != '/') ? STAT_ATTR : STAT_END;
    }
    break;
    }
    case STAT_ATTR:
    {
    xml_parser_parse_attrs(thiz, '/');
    state = STAT_END;

    break;
    }
    default:break;
    }

    if(state == STAT_END)
    {
    break;
    }
    }

    tag_name = thiz->buffer + (size_t)tag_name;
    /*解析完成,調用builder的函數xml_builder_on_start_element。*/
    xml_builder_on_start_element(thiz->builder, tag_name, (const char**)thiz->attrs);

    if(thiz->read_ptr[0] == '/')
    {
    /*如果tag以'/'結束,調用builder的函數xml_builder_on_end_element。*/
    xml_builder_on_end_element(thiz->builder, tag_name);
    }

    for(; *thiz->read_ptr != '>' && *thiz->read_ptr != '\0'; thiz->read_ptr++);

    return;
    }

    o 解析到結束tag時調用xml_builder_on_end_element

    static void xml_parser_parse_end_tag(XmlParser* thiz)
    {
    char* tag_name = NULL;
    const char* start = thiz->read_ptr;
    for(; *thiz->read_ptr != '\0'; thiz->read_ptr++)
    {
    if(*thiz->read_ptr == '>')
    {
    tag_name = thiz->buffer + xml_parser_strdup(thiz, start, thiz->read_ptr-start);
    /*解析完成,調用builder的函數xml_builder_on_end_element。*/
    xml_builder_on_end_element(thiz->builder, tag_name);

    break;
    }
    }

    return;
    }

    o 解析到文本時調用xml_builder_on_text

    static void xml_parser_parse_text(XmlParser* thiz)
    {
    const char* start = thiz->read_ptr - 1;
    for(; *thiz->read_ptr != '\0'; thiz->read_ptr++)
    {
    char c = *thiz->read_ptr;

    if(c == '<')
    {
    if(thiz->read_ptr > start)
    {
    /*解析完成,調用builder的函數xml_builder_on_text。*/
    xml_builder_on_text(thiz->builder, start, thiz->read_ptr-start);
    }
    thiz->read_ptr--;
    return;
    }
    else if(c == '&')
    {
    xml_parser_parse_entity(thiz);
    }
    }

    return;
    }

    o 解析到注釋時調用xml_builder_on_comment

    static void xml_parser_parse_comment(XmlParser* thiz)
    {
    enum _State
    {
    STAT_COMMENT,
    STAT_MINUS1,
    STAT_MINUS2,
    }state = STAT_COMMENT;

    const char* start = ++thiz->read_ptr;
    for(; *thiz->read_ptr != '\0'; thiz->read_ptr++)
    {
    char c = *thiz->read_ptr;

    switch(state)
    {
    case STAT_COMMENT:
    {
    if(c == '-')
    {
    state = STAT_MINUS1;
    }
    break;
    }
    case STAT_MINUS1:
    {
    if(c == '-')
    {
    state = STAT_MINUS2;
    }
    else
    {
    state = STAT_COMMENT;
    }
    break;
    }
    case STAT_MINUS2:
    {
    if(c == '>')
    {
    /*解析完成,調用builder的函數xml_builder_on_comment。*/
    xml_builder_on_comment(thiz->builder, start, thiz->read_ptr-start-2);
    return;
    }
    }
    default:break;
    }
    }

    return;
    }

    o 解析到處理指令時調用xml_builder_on_pi_element

    static void xml_parser_parse_pi(XmlParser* thiz)
    {
    enum _State
    {
    STAT_NAME,
    STAT_ATTR,
    STAT_END
    }state = STAT_NAME;

    char* tag_name = NULL;
    const char* start = thiz->read_ptr;

    for(; *thiz->read_ptr != '\0'; thiz->read_ptr++)
    {
    char c = *thiz->read_ptr;

    switch(state)
    {
    case STAT_NAME:
    {
    if(isspace(c) || c == '>')
    {
    tag_name = (char*)xml_parser_strdup(thiz, start, thiz->read_ptr - start);
    state = c != '>' ? STAT_ATTR : STAT_END;
    }

    break;
    }
    case STAT_ATTR:
    {
    xml_parser_parse_attrs(thiz, '?');
    state = STAT_END;
    break;
    }
    default:break;
    }

    if(state == STAT_END)
    {
    break;
    }
    }

    tag_name = thiz->buffer + (size_t)tag_name;
    /*解析完成,調用builder的函數xml_builder_on_pi_element。*/
    xml_builder_on_pi_element(thiz->builder, tag_name, (const char**)thiz->attrs);

    for(; *thiz->read_ptr != '>' && *thiz->read_ptr != '\0'; thiz->read_ptr++);

    return;
    }

    從上面的代碼可以看出,XmlParser在適當的時候調用了XmlBuilder的接口函數,至于XmlBuilder在這些函數里做什么,要看具體的Builder實現了。

    先看一個最簡單的XmlBuilder實現,它只是在屏幕上打印出傳遞給它的數據:

    o 創建函數

    XmlBuilder* xml_builder_dump_create(FILE* fp)
    {
    XmlBuilder* thiz = (XmlBuilder*)calloc(1, sizeof(XmlBuilder));

    if(thiz != NULL)
    {
    PrivInfo* priv = (PrivInfo*)thiz->priv;

    thiz->on_start_element = xml_builder_dump_on_start_element;
    thiz->on_end_element = xml_builder_dump_on_end_element;
    thiz->on_text = xml_builder_dump_on_text;
    thiz->on_comment = xml_builder_dump_on_comment;
    thiz->on_pi_element = xml_builder_dump_on_pi_element;
    thiz->on_error = xml_builder_dump_on_error;
    thiz->destroy = xml_builder_dump_destroy;

    priv->fp = fp != NULL ? fp : stdout;
    }

    return thiz;
    }

    和其它接口的創建函數一樣,它只是把接口要求的函數指針指到具體的實現函數上。

    o 實現 on_start_element

    static void xml_builder_dump_on_start_element(XmlBuilder* thiz, const char* tag, const char** attrs)
    {
    int i = 0;
    PrivInfo* priv = (PrivInfo*)thiz->priv;
    fprintf(priv->fp, "<%s", tag);

    for(i = 0; attrs != NULL && attrs[i] != NULL && attrs[i + 1] != NULL; i += 2)
    {
    fprintf(priv->fp, " %s=\"%s\"", attrs[i], attrs[i + 1]);
    }
    fprintf(priv->fp, ">");

    return;
    }

    o 實現on_end_element

    static void xml_builder_dump_on_end_element(XmlBuilder* thiz, const char* tag)
    {
    PrivInfo* priv = (PrivInfo*)thiz->priv;
    fprintf(priv->fp, "\n", tag);

    return;
    }

    o 實現on_text

    static void xml_builder_dump_on_text(XmlBuilder* thiz, const char* text, size_t length)
    {
    PrivInfo* priv = (PrivInfo*)thiz->priv;
    fwrite(text, length, 1, priv->fp);

    return;
    }

    o 實現on_comment

    static void xml_builder_dump_on_comment(XmlBuilder* thiz, const char* text, size_t length)
    {
    PrivInfo* priv = (PrivInfo*)thiz->priv;
    fprintf(priv->fp, "\n");

    return;
    }

    o 實現on_pi_element

    static void xml_builder_dump_on_pi_element(XmlBuilder* thiz, const char* tag, const char** attrs)
    {
    int i = 0;
    PrivInfo* priv = (PrivInfo*)thiz->priv;
    fprintf(priv->fp, "fp, " %s=\"%s\"", attrs[i], attrs[i + 1]);
    }
    fprintf(priv->fp, "?>\n");

    return;
    }

    o 實現on_error

    static void xml_builder_dump_on_error(XmlBuilder* thiz, int line, int row, const char* message)
    {
    fprintf(stderr, "(%d,%d) %s\n", line, row, message);

    return;
    }

    上面的XmlBuilder實現簡單,而且有一定的實用價值,我一般都會先寫這樣一個Builder。它不但對于調試程序有不小的幫助,而且只要稍做修改,就可以把它改進成一個美化數據格式的小工具,不管原始數據的格式(當然要合符相應的語法規則)有多亂,你都能以一種比較好看的方式打印出來。

    下面我們再看一個比較復雜的XmlBuilder的實現,它根據接收的數據構建一棵XML樹。

    o 創建函數

    XmlBuilder* xml_builder_tree_create(void)
    {
    XmlBuilder* thiz = (XmlBuilder*)calloc(1, sizeof(XmlBuilder));

    if(thiz != NULL)
    {
    PrivInfo* priv = (PrivInfo*)thiz->priv;

    thiz->on_start_element = xml_builder_tree_on_start_element;
    thiz->on_end_element = xml_builder_tree_on_end_element;
    thiz->on_text = xml_builder_tree_on_text;
    thiz->on_comment = xml_builder_tree_on_comment;
    thiz->on_pi_element = xml_builder_tree_on_pi_element;
    thiz->on_error = xml_builder_tree_on_error;
    thiz->destroy = xml_builder_tree_destroy;

    priv->root = xml_node_create_normal("__root__", NULL);
    priv->current = priv->root;
    }

    return thiz;
    }

    和其它接口的創建函數一樣,它只是把接口要求的函數指針指到具體的實現函數上。這里還創建了一個根結點__root__,以保證整棵樹只有一個根結點。

    o 實現 on_start_element

    static void xml_builder_tree_on_start_element(XmlBuilder* thiz, const char* tag, const char** attrs)
    {
    XmlNode* new_node = NULL;
    PrivInfo* priv = (PrivInfo*)thiz->priv;

    new_node = xml_node_create_normal(tag, attrs);
    xml_node_append_child(priv->current, new_node);
    priv->current = new_node;

    return;
    }

    這里創建了一個新的結點,并追加為priv->current的子結點,然后讓priv->current指向新的結點。

    o 實現 on_end_element

    static void xml_builder_tree_on_end_element(XmlBuilder* thiz, const char* tag)
    {
    PrivInfo* priv = (PrivInfo*)thiz->priv;
    priv->current = priv->current->parent;
    assert(priv->current != NULL);

    return;
    }

    這里只是讓priv->current指向它的父結點。

    o 實現 on_text

    static void xml_builder_tree_on_text(XmlBuilder* thiz, const char* text, size_t length)
    {
    XmlNode* new_node = NULL;
    PrivInfo* priv = (PrivInfo*)thiz->priv;

    new_node = xml_node_create_text(text);
    xml_node_append_child(priv->current, new_node);

    return;
    }

    這里創建一個文本結點, 并追加為priv->current的子結點。

    o 實現 on_comment

    static void xml_builder_tree_on_comment(XmlBuilder* thiz, const char* text, size_t length)
    {
    XmlNode* new_node = NULL;
    PrivInfo* priv = (PrivInfo*)thiz->priv;

    new_node = xml_node_create_comment(text);
    xml_node_append_child(priv->current, new_node);

    return;
    }

    這里創建一個注釋結點, 并追加為priv->current的子結點。

    o 實現 on_pi_element

    static void xml_builder_tree_on_pi_element(XmlBuilder* thiz, const char* tag, const char** attrs)
    {
    XmlNode* new_node = NULL;
    PrivInfo* priv = (PrivInfo*)thiz->priv;

    new_node = xml_node_create_pi(tag, attrs);
    xml_node_append_child(priv->current, new_node);

    return;
    }

    這里創建一個處理指令結點, 并追加為priv->current的子結點。

    o 實現on_error

    static void xml_builder_tree_on_error(XmlBuilder* thiz, int line, int row, const char* message)
    {
    fprintf(stderr, "(%d,%d) %s\n", line, row, message);

    return;
    }

    下面我們再看XmlNode的數據結構和主要函數:

    o 數據結構

    typedef struct _XmlNode
    {
    XmlNodeType type;
    union
    {
    char* text;
    char* comment;
    XmlNodePi pi;
    XmlNodeNormal normal;
    }u;
    struct _XmlNode* parent;
    struct _XmlNode* children;
    struct _XmlNode* sibling;
    }XmlNode;

    type決定了結點的類型,可以是處理指令(XML_NODE_PI)、文本(XML_NODE_TEXT)、注釋(XML_NODE_COMMENT)或普通TAG(XML_NODE_NORMAL)。

    聯合體用于存放具體結點信息。

    parent指向父結點。

    children指向第一個子結點。

    sibling指向下一個兄弟結點。

    o 創建普通TAG結點

    XmlNode* xml_node_create_normal(const char* name, const char** attrs)
    {
    XmlNode* node = NULL;
    return_val_if_fail(name != NULL, NULL);

    if((node = calloc(1, sizeof(XmlNode))) != NULL)
    {
    int i = 0;
    node->type = XML_NODE_NORMAL;
    node->u.normal.name = strdup(name);

    if(attrs != NULL)
    {
    for(i = 0; attrs[i] != NULL && attrs[i+1] != NULL; i += 2)
    {
    xml_node_append_attr(node, attrs[i], attrs[i+1]);
    }
    }
    }

    return node;
    }

    o 創建處理指令結點

    XmlNode* xml_node_create_pi(const char* name, const char** attrs)
    {
    XmlNode* node = NULL;
    return_val_if_fail(name != NULL, NULL);

    if((node = calloc(1, sizeof(XmlNode))) != NULL)
    {
    int i = 0;
    node->type = XML_NODE_PI;
    node->u.pi.name = strdup(name);
    if(attrs != NULL)
    {
    for(i = 0; attrs[i] != NULL && attrs[i+1] != NULL; i += 2)
    {
    xml_node_append_attr(node, attrs[i], attrs[i+1]);
    }
    }
    }

    return node;
    }

    o 創建文本結點

    XmlNode* xml_node_create_text(const char* text)
    {
    XmlNode* node = NULL;
    return_val_if_fail(text != NULL, NULL);

    if((node = calloc(1, sizeof(XmlNode))) != NULL)
    {
    node->type = XML_NODE_TEXT;
    node->u.text = strdup(text);
    }

    return node;
    }

    o 創建注釋結點

    XmlNode* xml_node_create_comment(const char* comment)
    {
    XmlNode* node = NULL;
    return_val_if_fail(comment != NULL, NULL);

    if((node = calloc(1, sizeof(XmlNode))) != NULL)
    {
    node->type = XML_NODE_COMMENT;
    node->u.comment = strdup(comment);
    }

    return node;
    }

    o 追加一個兄弟結點

    XmlNode* xml_node_append_sibling(XmlNode* node, XmlNode* sibling)
    {
    return_val_if_fail(node != NULL && sibling != NULL, NULL);

    if(node->sibling == NULL)
    {
    /*沒有兄弟結點,讓兄弟結點指向sibling */
    node->sibling = sibling;
    }
    else
    {
    /*否則,把sibling追加為最后一個兄弟結點*/
    XmlNode* iter = node->sibling;
    while(iter->sibling != NULL) iter = iter->sibling;
    iter->sibling = sibling;
    }
    /*讓兄弟結點的父結點指向自己的父結點*/

    sibling->parent = node->parent;

    return sibling;
    }

    o 追加一個子結點

    XmlNode* xml_node_append_child(XmlNode* node, XmlNode* child)
    {
    return_val_if_fail(node != NULL && child != NULL, NULL);

    if(node->children == NULL)
    {
    /*沒有子結點,讓子結點指向child */
    node->children = child;
    }
    else
    {
    /*否則,把child 追加為最后一個子結點*/
    XmlNode* iter = node->children;
    while(iter->sibling != NULL) iter = iter->sibling;
    iter->sibling = child;
    }
    /*讓子結點的父結點指向自己*/

    child->parent = node;

    return child;
    }

    回頭再看一下XmlParser,XmlBuilder及幾個具體的XmlBuilder的實現,我們可以看到,它們的實現都非常簡單,其實這完全得益于Builder模式的設計方法。它利用分而治之的思想,把數據的解析和數據的處理分開,降低了實現的復雜度。其次它利用了抽象的思想,從而數據的解析只關心處理數據處理的接口,而不關心的它的實現,使得數據解析和數據處理可以獨立變化。

    分而治之和抽象是降低復雜度最有效的手段之一,它們在Builder模式里得到了很好的體現。初學者應該多花些時間去體會。


    文章出處:http://www.limodev.cn/blog
    作者聯系方式:李先靜 <xianjimli at hotmail dot com>

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

    主站蜘蛛池模板: 亚洲精品国产免费| 91在线亚洲精品专区| 免费看美女午夜大片| 精品国产精品久久一区免费式| 亚洲国产av一区二区三区丶| 最近中文字幕mv免费高清视频8 | 亚洲人午夜射精精品日韩| 最新亚洲人成网站在线观看| 国产精品免费电影| 色多多A级毛片免费看| 国产亚洲老熟女视频| 日韩电影免费观看| 久久久久亚洲AV片无码下载蜜桃| 无码精品国产一区二区三区免费 | 国产免费131美女视频| 四虎影视永久在线精品免费| 亚洲国产婷婷香蕉久久久久久| a级片免费在线播放| 亚洲熟妇色自偷自拍另类| 无码国产精品久久一区免费 | 亚洲中文字幕久久久一区| 亚洲成?v人片天堂网无码| a级片免费在线观看| 亚洲卡一卡2卡三卡4麻豆| 色播在线永久免费视频| 一区二区视频在线免费观看| 亚洲国产精品一区| 成年人在线免费看视频| 亚洲精品黄色视频在线观看免费资源| 亚洲av无码不卡一区二区三区| 麻豆国产精品免费视频| 真正全免费视频a毛片| 亚洲AV乱码一区二区三区林ゆな| 噼里啪啦电影在线观看免费高清| 人妻无码中文字幕免费视频蜜桃 | 男人天堂2018亚洲男人天堂| 国产99视频精品免费视频7| 无码少妇精品一区二区免费动态 | 高清免费久久午夜精品| 777亚洲精品乱码久久久久久| 日韩在线视频免费看|