首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Expat XML 解析庫完整指南:SAX 事件流模型、API 詳解與高效能串流解析實戰

     編輯:關於C語言

一、Expat 簡介

Expat (James Clark) 是 C 語言流式 XML 解析器,採用 SAX (Simple API for XML) 事件驅動模型:解析器讀取輸入流,遇到開始標籤、結束標籤、字元資料等事件時呼叫使用者註冊的回呼函數。特點:

  • 非驗證 (預設不載入 DTD/Schema),輕量、快速。
  • 支援多種編碼 (UTF-8, UTF-16, ISO-8859-1 等)。
  • 無 DOM 樹記憶體開銷,適合大檔案/嵌入式/串流處理。
  • C 標準庫風格 API,可移植性極強。

二、核心 API

#include 

// 建立解析器
XML_Parser XML_ParserCreate(const char *encoding); // NULL 為 UTF-8
XML_Parser XML_ParserCreateNS(const char *encoding, char ns_sep); // 命名空間支援

// 註冊回呼
void XML_SetElementHandler(XML_Parser p,
    void (*start)(void *userData, const char *name, const char **atts),
    void (*end)(void *userData, const char *name));

void XML_SetCharacterDataHandler(XML_Parser p,
    void (*handler)(void *userData, const char *s, int len));

void XML_SetUserData(XML_Parser p, void *userData);

// 解析 (可分塊)
enum XML_Status XML_Parse(XML_Parser p, const char *buf, int len, int isFinal);

// 釋放
void XML_ParserFree(XML_Parser p);

// 錯誤處理
enum XML_Error XML_GetErrorCode(XML_Parser p);
const char *XML_ErrorString(enum XML_Error code);
long XML_GetCurrentLineNumber(XML_Parser p);
long XML_GetCurrentColumnNumber(XML_Parser p);

三、完整範例:XML 結構列印器

// xml_print.c
#include 
#include 
#include 

static int depth = 0;

void XMLCALL startElement(void *userData, const char *name, const char **atts) {
    for (int i = 0; i < depth; i++) putchar('\t');
    printf("%s", name);
    for (int i = 0; atts[i]; i += 2)
        printf(" %s='%s'", atts[i], atts[i+1]);
    putchar('\n');
    depth++;
}

void XMLCALL endElement(void *userData, const char *name) {
    depth--;
}

void XMLCALL charData(void *userData, const char *s, int len) {
    // 過濾純空白
    for (int i = 0; i < len; i++)
        if (s[i] != ' ' && s[i] != '\n' && s[i] != '\t' && s[i] != '\r') {
            for (int j = 0; j < depth; j++) putchar('\t');
            printf("#text: %.*s\n", len, s);
            break;
        }
}

int main(int argc, char **argv) {
    if (argc != 2) { fprintf(stderr, "Usage: %s file.xml\n", argv[0]); return 1; }
    
    FILE *fp = fopen(argv[1], "r");
    if (!fp) { perror("fopen"); return 1; }
    
    XML_Parser parser = XML_ParserCreate(NULL);
    XML_SetUserData(parser, &depth);
    XML_SetElementHandler(parser, startElement, endElement);
    XML_SetCharacterDataHandler(parser, charData);
    
    char buf[4096];
    int done, len;
    do {
        len = fread(buf, 1, sizeof(buf), fp);
        done = feof(fp);
        if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) {
            fprintf(stderr, "Parse error at line %ld: %s\n",
                    XML_GetCurrentLineNumber(parser),
                    XML_ErrorString(XML_GetErrorCode(parser)));
            XML_ParserFree(parser);
            fclose(fp);
            return 1;
        }
    } while (!done);
    
    XML_ParserFree(parser);
    fclose(fp);
    return 0;
}

四、編譯與執行

gcc -o xml_print xml_print.c -lexpat
./xml_print sample.xml

五、命名空間處理

XML_Parser parser = XML_ParserCreateNS(NULL, '|'); // 分隔符
// 回呼中 name 格式: "namespaceURI|localName"
// 如: "https://www.w3.org/1999/xhtml|div"
// atts 陣列同樣包含命名空間前綴

六、進階技巧

  • 外部實體載入:XML_SetExternalEntityRefHandler 自定義 DTD/實體解析,預設不載入外部資源 (安全)。
  • 未知編碼:XML_SetUnknownEncodingHandler 支援自定義編碼映射表。
  • 緩衝區大小:XML_SetBufferSize(parser, 16384) 調整內部緩衝。
  • 停止解析:回呼中 XML_StopParser(parser, XML_FALSE) 非致命停止,XML_TRUE 致命錯誤。
  • 記憶體管理:Expat 內部分配,解析器釋放時自動清理;使用者資料自行管理。

七、Expat vs libxml2 SAX vs 自寫解析器

特性Expatlibxml2 SAX自寫 (正則/手寫)
標準合規度高 (XML 1.0/1.1)極高 (含 XML Schema, XPath)低 (易遺漏邊界情況)
效能極快、低記憶體快、功能豐富稍重視實作而定
依賴無 (純 C)libxml2 + 圖庫依賴無
API 風格C 回呼C 回呼 (較複雜)任意
適用場景嵌入式、大檔案串流、簡單提取完整驗證、複雜查詢、轉換極簡固定格式、極度受限環境

八、常見錯誤與避坑

  1. 分塊解析中斷標籤:Expat 內部緩衝自動處理跨緩衝區標籤,但字元資料可能分塊回呼,需自行拼接。
  2. 編碼不匹配:輸入非 UTF-8 時需指定 XML_ParserCreate("GBK") 或確保檔案有 XML 宣告 。
  3. 實體引用:&lt; 等預定義實體自動展開;數值實體 Ӓ 自動轉換;外部實體需註冊處理器。
  4. 大小寫敏感:XML 標籤大小寫敏感,回呼中 name 保持原樣。
  5. 執行緒安全:單一 XML_Parser 非執行緒安全,多執行緒需各自建立解析器。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved