Expat (James Clark) 是 C 語言流式 XML 解析器,採用 SAX (Simple API for XML) 事件驅動模型:解析器讀取輸入流,遇到開始標籤、結束標籤、字元資料等事件時呼叫使用者註冊的回呼函數。特點:
#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_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 | libxml2 SAX | 自寫 (正則/手寫) |
|---|---|---|---|
| 標準合規度 | 高 (XML 1.0/1.1) | 極高 (含 XML Schema, XPath) | 低 (易遺漏邊界情況) |
| 效能 | 極快、低記憶體 | 快、功能豐富稍重 | 視實作而定 |
| 依賴 | 無 (純 C) | libxml2 + 圖庫依賴 | 無 |
| API 風格 | C 回呼 | C 回呼 (較複雜) | 任意 |
| 適用場景 | 嵌入式、大檔案串流、簡單提取 | 完整驗證、複雜查詢、轉換 | 極簡固定格式、極度受限環境 |
XML_ParserCreate("GBK") 或確保檔案有 XML 宣告 。< 等預定義實體自動展開;數值實體 Ӓ 自動轉換;外部實體需註冊處理器。name 保持原樣。XML_Parser 非執行緒安全,多執行緒需各自建立解析器。