美團龍貓利用expat庫實現的保存xml指定范圍數據到csv的C程序

用自己代碼逐個字符解析的速度較慢,嘗試了libxml2也比較慢,它需要一次性讀入內存,而expat庫支持流式讀取。就讓龍貓寫了一個程序,畢竟是久經考驗的庫,程序很快就調試通過了。要不是我一開始沒信心,讓他先輸出10行試試,還能少走很多彎路。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <expat.h>#define MAX_CELL_CONTENT 256// 解析范圍
typedef struct {int start_row;int end_row;char start_col;char end_col;
} ParseRange;// 全局結果存儲
typedef struct {int row;char col;char value[MAX_CELL_CONTENT];
} CellResult;// 范圍檢查
int is_cell_in_range(int row, char col, ParseRange range) {return (row >= range.start_row && row <= range.end_row &&col >= range.start_col && col <= range.end_col);
}
// 全局結果存儲(動態數組)
typedef struct {CellResult *data;     // 動態數組int count;            // 當前數量int capacity;         // 當前容量
} DynamicResults;DynamicResults all_results = {0}; // 全局變量// 初始化動態數組
void init_results() {all_results.capacity = 1024;  // 初始容量all_results.data = malloc(all_results.capacity * sizeof(CellResult));all_results.count = 0;
}// 擴容動態數組
void ensure_capacity(int needed) {if (all_results.count + needed >= all_results.capacity) {all_results.capacity *= 2;  // 翻倍擴容all_results.data = realloc(all_results.data, all_results.capacity * sizeof(CellResult));}
}// 添加結果(無限制版本)
void add_cell_result(int row, char col, const char *value, int is_empty) {ensure_capacity(1);  // 確保有空間all_results.data[all_results.count].row = row;all_results.data[all_results.count].col = col;strncpy(all_results.data[all_results.count].value, value, MAX_CELL_CONTENT - 1);all_results.count++;
}// 釋放內存
void free_results() {free(all_results.data);all_results.data = NULL;all_results.count = all_results.capacity = 0;
}// 解析Excel范圍 (如"A1:Z100")
int parse_excel_range(const char *range_str, ParseRange *range) {if (sscanf(range_str, "%c%d:%c%d", &range->start_col, &range->start_row,&range->end_col, &range->end_row) != 4) {return -1;}if (range->start_col > range->end_col) return -1;if (range->start_row > range->end_row) return -1;return 0;
}// 解析器狀態
typedef struct {ParseRange range;int in_row;int current_row;char current_col;int value_started;char temp_value[MAX_CELL_CONTENT];int value_len;int rows_parsed;  // 已解析行數
} ParserState;// 開始標簽回調
void XMLCALL start_element(void *user_data, const XML_Char *name, const XML_Char **attrs) {ParserState *state = (ParserState*)user_data;if (strcmp(name, "row") == 0) {state->in_row = 1;state->current_row = -1;// 解析行號屬性for (int i = 0; attrs[i]; i += 2) {if (strcmp(attrs[i], "r") == 0) {state->current_row = atoi(attrs[i+1]);state->rows_parsed++;break;}}if(1==0)printf("解析行 %d\n", state->current_row);}else if (strcmp(name, "c") == 0 && state->in_row) {// 解析列屬性for (int i = 0; attrs[i]; i += 2) {if (strcmp(attrs[i], "r") == 0) {state->current_col = attrs[i+1][0];break;}}}else if (strcmp(name, "v") == 0 || strcmp(name, "t") == 0) {if (state->current_row >= state->range.start_row && state->current_row <= state->range.end_row) {state->value_started = 1;state->value_len = 0;state->temp_value[0] = '\0';}}
}// 文本內容回調
void XMLCALL character_data(void *user_data, const XML_Char *s, int len) {ParserState *state = (ParserState*)user_data;if (state->value_started && state->value_len + len < MAX_CELL_CONTENT - 1) {memcpy(state->temp_value + state->value_len, s, len);state->value_len += len;state->temp_value[state->value_len] = '\0';}
}// 結束標簽回調
void XMLCALL end_element(void *user_data, const XML_Char *name) {ParserState *state = (ParserState*)user_data;if (strcmp(name, "row") == 0) {state->in_row = 0;}else if ((strcmp(name, "v") == 0 || strcmp(name, "t") == 0) && state->value_started) {if (is_cell_in_range(state->current_row, state->current_col, state->range)) {if(1==0)printf("  單元格 %c%d: '%s'\n", state->current_col, state->current_row, state->temp_value);add_cell_result(state->current_row, state->current_col, state->temp_value, 0);}state->value_started = 0;}
}// 主解析函數
int parse_sheet_xml(const char *filename, ParseRange range) {XML_Parser parser = XML_ParserCreate(NULL);ParserState state = {0};state.range = range;XML_SetUserData(parser, &state);XML_SetElementHandler(parser, start_element, end_element);XML_SetCharacterDataHandler(parser, character_data);FILE *file = fopen(filename, "rb");if (!file) {printf("錯誤: 無法打開文件 %s\n", filename);XML_ParserFree(parser);return -1;}char buffer[8192];int done;do {size_t len = fread(buffer, 1, sizeof(buffer), file);done = (len < sizeof(buffer));if (XML_Parse(parser, buffer, len, done) == XML_STATUS_ERROR) {printf("解析錯誤: %s (行 %d)\n", XML_ErrorString(XML_GetErrorCode(parser)),XML_GetCurrentLineNumber(parser));break;}} while (!done);fclose(file);XML_ParserFree(parser);return 0;
}
/*** 輸出CSV文件* @param filename 輸出文件名*/
int save_results_to_csv(const char *filename) {FILE *csv = fopen(filename, "w");if (!csv) {printf("錯誤: 無法創建CSV文件 %s\n", filename);return -1;}// 計算列范圍char min_col = all_results.data[0].col;char max_col = all_results.data[0].col;for (int i = 1; i < all_results.count; i++) {if (all_results.data[i].col < min_col) min_col = all_results.data[i].col;if (all_results.data[i].col > max_col) max_col = all_results.data[i].col;}// 輸出標題fprintf(csv, "Row");for (char col = min_col; col <= max_col; col++) {fprintf(csv, ",%c", col);}    fprintf(csv, "\n");// 數據行int current_row = all_results.data[0].row;int row_start_idx = 0;for (int i = 0; i < all_results.count; i++) {	if (all_results.data[i].row != current_row) {// 輸出當前行fprintf(csv, "%d", current_row);for (char col = min_col; col <= max_col; col++) {int found = 0;for (int j = row_start_idx; j < i; j++) {if (all_results.data[j].col == col) {fprintf(csv, ",%s", all_results.data[j].value);found = 1;break;}}if (!found) fprintf(csv, ",");}fprintf(csv, "\n");// 下一行current_row = all_results.data[i].row;row_start_idx = i;}}fclose(csv);printf("CSV已保存到 %s\n", filename);return 0;
}// 主函數
int main(int argc, char *argv[]) {if (argc != 3) {printf("用法: %s <xml文件> <范圍(A1:Z100)>\n", argv[0]);return 1;}ParseRange range;if (parse_excel_range(argv[2], &range) != 0) {printf("錯誤: 無效范圍格式,應為 A1:Z100\n");return 1;}printf("解析范圍: %c%d:%c%d\n", range.start_col, range.start_row, range.end_col, range.end_row);init_results();   if (parse_sheet_xml(argv[1], range) == 0) {// 生成CSV文件名(替換.xml為.csv)char csv_filename[256];strncpy(csv_filename, argv[1], sizeof(csv_filename) - 1);char *ext = strrchr(csv_filename, '.');if (ext) strcpy(ext, ".csv");else strncat(csv_filename, ".csv", sizeof(csv_filename) - strlen(csv_filename) - 1);// 輸出CSVsave_results_to_csv(csv_filename);}   free_results();   return 0;
}

編譯執行

gcc -o expatxml3 expatxml3.c -lexpat -O3
time ./expatxml3 lineitem/xl/worksheets/sheet1.xml A1:Z10000
解析范圍: A1:Z10000
CSV已保存到 lineitem/xl/worksheets/sheet1.csvreal    0m6.508s
user    0m2.132s
sys     0m0.392s
time ./expatxml3 lineitem/xl/worksheets/sheet1.xml A100001:Z110000
解析范圍: A100001:Z110000
CSV已保存到 lineitem/xl/worksheets/sheet1.csvreal    0m6.534s
user    0m2.111s
sys     0m0.431s
time ./expatxml3 lineitem/xl/worksheets/sheet1.xml A1:Z1000000
解析范圍: A1:Z1000000
CSV已保存到 lineitem/xl/worksheets/sheet1.csvreal    0m10.207s
user    0m3.046s
sys     0m1.795s
time ./expatxml3 lineitem/xl/worksheets/sheet1.xml A300000:Z660000
解析范圍: A300000:Z660000
CSV已保存到 lineitem/xl/worksheets/sheet1.csvreal    0m9.378s
user    0m2.574s
sys     0m1.030s

針對60萬行16列,300MB xml, 這個時間還不錯,但是沒有考慮sharedstrings.xml,否則會慢一些。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/95736.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/95736.shtml
英文地址,請注明出處:http://en.pswp.cn/web/95736.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Transformer核心—自注意力機制

Transformer基礎—自注意力機制 當我們處理文本、語音這類序列數據時&#xff0c;總會遇到一個老問題&#xff1a;模型到底該怎么理解“前后文”呢&#xff1f; RNN 和 LSTM 曾經是熱門的答案&#xff0c;它們沿著時間順序一點點地讀數據&#xff0c;但讀得太慢&#xff0c;還容…

分片上傳-

分片上傳原理&#xff1a;客戶端將選擇的文件進行切分&#xff0c;每一個分片都單獨發送請求到服務端&#xff1b;斷點續傳 & 秒傳原理&#xff1a;客戶端 發送請求詢問服務端某文件的上傳狀態 &#xff0c;服務端響應該文件已上傳分片&#xff0c;客戶端再將未上傳分片上傳…

零知開源——基于STM32F103RBT6的智能風扇控制系統設計與實現

?零知IDE 是一個真正屬于國人自己的開源軟件平臺&#xff0c;在開發效率上超越了Arduino平臺并且更加容易上手&#xff0c;大大降低了開發難度。零知開源在軟件方面提供了完整的學習教程和豐富示例代碼&#xff0c;讓不懂程序的工程師也能非常輕而易舉的搭建電路來創作產品&am…

ReACT Agent概述

目錄 1. 核心思想&#xff1a;解決傳統方法的局限性 2. ReACT 的工作原理&#xff1a;一個循環過程 3. 技術實現的關鍵要素 4. ReACTAgent 在任務中的具體工作流程 5. 優勢與重要性 6. 挑戰與局限性 總結 ReACT 是一個非常重要的框架&#xff0c;它代表了構建能夠推理&a…

必知!機器人的分類與應用:RPA、人形與工業機器人

每當提及“機器人”這三個字&#xff0c;許多人的第一反應或許仍是科幻電影中那種具備人類外形、可自由行走與對話的仿生裝置。然而&#xff0c;一個值得深入探討的科技現實是&#xff1a;我們對于人形機器人的迷戀&#xff0c;更多源自文化敘事與情感投射&#xff0c;而非真實…

最快的 C 語言 JSON 庫 - yyjson

文章目錄DOM 模式下的性能比對一、AWS EC2 (AMD EPYC 7R32, gcc 9.3)二、iPhone (Apple A14, clang 12)持續更新中 持續更新中 持續更新中一個用 ANSI C(C89) 編寫的高性能 JSON 庫 API.md DOM 模式下的性能比對 DOM 模式&#xff0c;即構建完整 JSON 內存結構后訪問數據的模…

TP8 模型save更新不成功

一、User文件頭部代碼class User extends Model {const TITLE_NAME 用戶;//名稱//不能刪除protected $name user_; //表名 protected $connection \app\services\database\model\DbConnModel::CONN_DB_SITE; //數據庫的連接二、更新部分我要更新user_1用戶表中的用戶信息$se…

中囯移動電視盒子(魔百和)B860AV2.1-A2和CM311-5-zg刷機手記

文章目錄B860AV2.1-A2電視盒子情況打開隱藏或屏蔽的功能進入Recovery模式打開WiFi&#xff08;如果被隱藏&#xff09;打開運維調試打開ADB調試安裝第三方應用、設置第三方桌面等&#xff08;Fiddler抓包替換官方App安裝包&#xff09;開啟ADB和使用ADB禁止“首次啟動設置”刷機…

【系統架構設計(14)】項目管理下:軟件質量與配置管理:構建可靠軟件的基礎保障

文章目錄一、核心思想二、軟件質量屬性&#xff1a;定義"好軟件"的標準三、質量保證與控制&#xff1a;實現質量標準的方法四、CMMI模型&#xff1a;組織質量能力的演進路徑五、軟件配置管理&#xff1a;質量成果的保護機制六、軟件工具&#xff1a;質量管理的技術支…

碼農的“必修課”:深度解析Rust的所有權系統(與C++內存模型對比)

在軟件開發的世界里&#xff0c;內存管理是至關重要的一個環節。它是程序運行的基礎&#xff0c;直接關系到程序的性能、穩定性和安全性。一個糟糕的內存管理策略&#xff0c;可能導致內存泄漏、野指針、緩沖區溢出等一系列令人頭疼的問題&#xff0c;甚至帶來災難性的安全漏洞…

Java全棧學習筆記30

# MySQL 卸載安裝版電腦管家/360/控制面板卸載mysql服務即可刪除ProgramData中的MySQL目錄解壓版winr 輸入 services.msc 打開服務管理。查看是否存在MySQL&#xff0c;如果存在則刪除注冊表 winR regedit 打開注冊表計算機\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Servic…

Transformers 學習入門:前置知識補漏

在學習 Transformers 之前&#xff0c;打好神經網絡和自然語言處理的基礎至關重要。本文整理了需要掌握的核心前置知識&#xff0c;用通俗的例子幫你快速理解復雜概念&#xff0c;為后續學習鋪平道路。? 一、神經網絡基礎? 1. 多層感知機&#xff08;MLP&#xff09;&#xf…

雙攝工業相機的主要特點和應用場景

雙攝工業相機&#xff08;雙目攝像頭&#xff09;在工業領域中的應用非常廣泛&#xff0c;其核心優勢在于通過雙鏡頭模擬人眼立體視覺&#xff0c;能夠獲取深度信息并實現高精度三維重建。 一、雙攝工業相機的核心優勢 深度感知與三維重建 雙目攝像頭通過兩個鏡頭從不同角度拍…

YOLOv11改進:FocalModulation替換SPPF(精度更高的空間金字塔池化)

YOLOv11&#xff1a;FocalModulation替換SPPF&#xff08;精度更高的空間金字塔池化&#xff09; 引言 在目標檢測領域&#xff0c;YOLO系列算法以其高效性和準確性廣受歡迎。作為YOLO系列的最新成員之一&#xff0c;YOLOv11在多個方面進行了優化和改進。其中&#xff0c;空間金…

LLM與數據工程的融合:衡石Data Agent的語義層與Agent框架設計

在數字經濟浪潮中&#xff0c;企業數據智能正經歷從"工具輔助"到"智能協同"的范式躍遷。傳統BI系統受限于靜態報表與預設指標&#xff0c;難以應對動態業務場景的復雜需求。衡石科技發布的HENGSHI SENSE 6.0通過"Data AI Agent"架構創新&#x…

假設一個算術表達式中包含圓括號、方括號和花括號3種類型的括號,編寫一個算法來判別,表達式中的括號是否配對,以字符“\0“作為算術表達式的結束符

思想:這道題是棧的應用類型&#xff0c;我們可以建立一個棧來保存(,[,{,通過遍歷字符串如果是三個左括號其中一個則入棧&#xff0c;當遇到)]}則出棧配對&#xff0c;如果左右匹配&#xff0c;則遍歷下一個元素&#xff0c;如果不匹配直接返回&#xff0c;如果遍歷字符串結束&a…

鴻蒙Next的UI國際化與無障礙適老化實踐:構建全球包容的數字世界

科技不應讓任何人掉隊&#xff0c;鴻蒙Next正將這一理念變為現實在全球化日益深入的今天&#xff0c;應用的國際化與無障礙設計不再是"錦上添花"&#xff0c;而是不可或缺的核心競爭力。華為鴻蒙Next系統從設計之初就深入考慮了這些需求&#xff0c;為開發者提供了完…

深度學習——遷移學習

遷移學習作為深度學習領域的一項革命性技術&#xff0c;正在重塑我們構建和部署AI模型的方式。本文將帶您深入探索遷移學習的核心原理、詳細實施步驟以及實際應用中的關鍵技巧&#xff0c;幫助您全面掌握這一強大工具。遷移學習的本質與價值遷移學習的核心思想是"站在巨人…

RAG|| LangChain || LlamaIndex || RAGflow

大模型&#xff1a;預訓練模型 外掛知識庫&#xff1a;知識庫->向量數據庫 輸入-》預處理成向量 提示詞-》llm歸納總結 離線&#xff1a;企業原文本存到向量數據庫 向量&#xff1a; 同一個向量模型&#xff08;第二代檢索&#xff0c;推薦&#xff0c;個人助理&#xff0c;…

mcp_clickhouse代碼學習

引言:當ClickHouse遇上MCP 作為一個基于Model Context Protocol(MCP)框架的ClickHouse查詢服務器,mcp_clickhouse不僅在技術實現上展現了優雅的設計思路,更在架構層面提供了許多值得借鑒的解決方案。 一、項目概覽:架構初探 mcp_clickhouse是一個專為ClickHouse數據庫設計…