当前位置: 首页 > web >正文

美团龙猫(longcat.AI)编写的利用二分查找优化Excel的sheet.xml指定范围输出C程序

大约对话了5次,得出了正确的结果,一开始他用二分找结束位置,导致解析为空。以及未考虑自闭合的单元格标签,导致坐标和值对应错误。

提示词

用C语言实现使用二分查找法优化xlsx的sheet.xml解析,快速定位要解析的数据范围,并跳过无需解析部分。具体是:在1/2文件长度部分读取<row标签,解析r="坐标",与用户输入的范围左上角坐标或首行行号比较,二分定位,直到找到相等的行,开始解析,一边检查是否到达末行,到达文件末或行末结束解析。单元格标签也有r="坐标"属性,如果在用户输入的范围内则解析,否则跳过。示例数据如下:

<row r="3">
<c r="B3" s="1" t="s">
<v>0</v>
</c>
<c r="D3" s="4" t="s">
<v>3</v>
</c>
<c r="F3" s="5" t="s">
<v>4</v>
</c>
<c r="H3" s="6" t="s">
<v>5</v>
</c>
</row>
<row r="4">
<c r="D4" s="4"/>
<c r="F4" s="5"/>

不考虑二分找结束位置,找到首行就直接开始解析输出。

<c r="D13" s="13"/>这种形式的单元格,虽然没有</c>,但/>也表示单元格结束,请增加这个逻辑。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>#define MAX_LINE_LENGTH 4096
#define MAX_CELL_CONTENT 1024
#define MAX_SHEET_ROWS 1048576 // Excel最大行数// 用户输入范围
typedef struct {int start_row;int end_row;char start_col; // 列字母,如'A'char end_col;   // 列字母,如'Z'
} ParseRange;// 解析结果
typedef struct {int row;char col;char value[MAX_CELL_CONTENT];int is_empty; // 空单元格标记
} CellData;// 全局变量
CellData *results = NULL;
int result_count = 0;
int result_capacity = 0;// 函数声明
int binary_search_start(FILE *file, ParseRange range);
int find_row_by_binary_search(FILE *file, int target_row, long *start_pos, long *end_pos);
void parse_row_data(FILE *file, ParseRange range, long start_pos, long end_pos);
void add_cell_result(int row, char col, const char *value, int is_empty);
int is_cell_in_range(int row, char col, ParseRange range);
int compare_row_col(int row1, char col1, int row2, char col2);
void free_results();/*** 主解析函数 - 修正版本* @param filename XML文件路径* @param range 解析范围* @return 0成功,-1失败*/
int parse_sheet_xml(const char *filename, ParseRange range) {FILE *file = fopen(filename, "r");if (!file) {perror("无法打开文件");return -1;}// 初始化结果数组result_count = 0;result_capacity = 1024;results = (CellData *)malloc(result_capacity * sizeof(CellData));if (!results) {fclose(file);return -1;}// 二分查找起始行if (binary_search_start(file, range)) {// 直接开始解析,从当前位置开始char buffer[MAX_LINE_LENGTH];long row_start_pos = ftell(file);// 向前查找最近的<row标签开始位置for (long pos = row_start_pos; pos >= 0; pos--) {fseek(file, pos, SEEK_SET);if (fgetc(file) == '<') {// 检查是否是<row标签int is_row_tag = 1;for (int i = 1; i < 4; i++) {if (fgetc(file) != "row"[i]) {is_row_tag = 0;break;}}if (is_row_tag) {row_start_pos = pos;  // 记录<row标签的起始位置fseek(file, pos, SEEK_SET); // 定位到<row标签开始break;}}}// 获取文件大小作为结束边界long file_size;fseek(file, 0, SEEK_END);file_size = ftell(file);// 定位到<row标签开始位置,准备解析fseek(file, row_start_pos, SEEK_SET);// 直接解析数据 - 从<row标签开始到文件末尾parse_row_data(file, range, row_start_pos, file_size);}fclose(file);return 0;
}
/*        // 直接解析数据printf("ftell(file)=%d ", ftell(file));parse_row_data(file, range, ftell(file), file_size);}fclose(file);return 0;
}
*//*** 二分查找定位起始行* @param file 文件指针* @param range 解析范围* @return 是否找到起始行*/
int binary_search_start(FILE *file, ParseRange range) {long file_size = 0;long low, high, mid;// 获取文件大小fseek(file, 0, SEEK_END);file_size = ftell(file);fseek(file, 0, SEEK_SET);low = 0;high = file_size;int last_found_row = -1;long last_found_pos = -1;while (low <= high) {mid = (low + high) / 2;fseek(file, mid, SEEK_SET);// 向前查找最近的<row标签char buffer[MAX_LINE_LENGTH];long row_start_pos = -1;int row_num = -1;// 从mid位置向前扫描,找到前一个<row标签for (long pos = mid; pos >= low && pos >= 0; pos--) {fseek(file, pos, SEEK_SET);if (fgetc(file) == '<') {if (pos + 4 <= file_size && fgetc(file) == 'r' && fgetc(file) == 'o' && fgetc(file) == 'w') {row_start_pos = pos;break;}}}// 如果向前没找到,从mid向后找if (row_start_pos == -1) {for (long pos = mid; pos <= high && pos < file_size - 4; pos++) {fseek(file, pos, SEEK_SET);if (fgetc(file) == '<') {if (pos + 4 <= file_size && fgetc(file) == 'r' && fgetc(file) == 'o' && fgetc(file) == 'w') {row_start_pos = pos;break;}}}}if (row_start_pos == -1) {// 没有找到<row标签if (mid == low) break;high = mid - 1;continue;}// 解析行号fseek(file, row_start_pos, SEEK_SET);while (fgets(buffer, MAX_LINE_LENGTH, file)) {if (strstr(buffer, "<row")) {char *row_attr = strstr(buffer, "r=\"");if (row_attr) {row_attr += 3; // 跳过r="row_num = atoi(row_attr);break;}}}if (row_num == -1) {// 解析行号失败,调整搜索范围if (row_start_pos < range.start_row) low = mid + 1;else high = mid - 1;continue;}if (row_num == range.start_row) {// 找到精确匹配last_found_row = row_num;last_found_pos = row_start_pos;break;} else if (row_num < range.start_row) {// 当前行小于目标行if (row_num > last_found_row) {last_found_row = row_num;last_found_pos = row_start_pos;}low = mid + 1;} else {// 当前行大于目标行high = mid - 1;}}// 如果找到了合适的起始位置if (last_found_row != -1) {//printf(" last_found_pos=%d\n",  last_found_pos);fseek(file, last_found_pos, SEEK_SET);return 1;}return 0;
}/*** 解析行数据,包括单元格* @param file 文件指针* @param range 解析范围* @param start_pos 起始位置* @param end_pos 结束位置*/
void parse_row_data(FILE *file, ParseRange range, long start_pos, long end_pos) {char buffer[MAX_LINE_LENGTH];char temp_value[MAX_CELL_CONTENT];int in_row = 0;int current_row = -1;char current_col = '\0';fseek(file, start_pos, SEEK_SET);while (fgets(buffer, MAX_LINE_LENGTH, file) && (long)ftell(file) <= end_pos) {char *pos = buffer;// 处理每行中的标签while ((pos = strchr(pos, '<')) != NULL) {if (strncmp(pos, "<row", 4) == 0) {// 解析行号char *row_attr = strstr(pos, "r=\"");if (row_attr) {row_attr += 3;current_row = atoi(row_attr);}in_row = 1;pos += 4;}else if (strncmp(pos, "</row>", 6) == 0) {// 行结束if (current_row >= range.end_row) {// 超过用户指定范围,停止解析return;}in_row = 0;current_row = -1;pos += 6;}
// 在parse_row_data函数中,修改单元格解析部分如下:else if (in_row && strncmp(pos, "<c ", 3) == 0) {// 解析单元格char *col_attr = strstr(pos, "r=\"");char *value_start = NULL;int is_empty = 0;int cell_has_value = 0;int is_self_closing = 0; // 新增:标记是否是自闭合标签char current_cell_col = '\0';if (col_attr) {col_attr += 3; // 跳过r="current_cell_col = col_attr[0]; // 获取当前单元格的列字母// 检查是否是自闭合标签 <c ... />char *self_close = strstr(pos, "/>");if (self_close) {is_self_closing = 1;}// 跳过列字母和数字分隔符while (isdigit(col_attr[0])) col_attr++;// 检查单元格值char *v_tag = NULL;char *cell_content = pos;v_tag = strstr(cell_content, "<v>");if (v_tag) {//printf("<v>  ");value_start = v_tag + 3;char *v_end = strstr(v_tag, "</v>");if (v_end) {*v_end = '\0';strncpy(temp_value, value_start, MAX_CELL_CONTENT - 1);temp_value[MAX_CELL_CONTENT - 1] = '\0';cell_has_value = 1;}} else if (!is_self_closing) {// 只有非自闭合标签才尝试读取更多内容int need_more_data = 0;int cell_content_len = strlen(cell_content);if (cell_content_len < MAX_LINE_LENGTH - 1) {need_more_data = 1;} else {if (cell_content[cell_content_len - 1] == '<' || (cell_content[cell_content_len - 1] == 'v' && cell_content[cell_content_len - 2] == '<')) {need_more_data = 1;}}if (need_more_data) {long current_file_pos = ftell(file);char extra_buffer[MAX_LINE_LENGTH];int extra_found = 0;while (fgets(extra_buffer, MAX_LINE_LENGTH, file) && !strstr(extra_buffer, "</c>") && !strstr(extra_buffer, "</row>")) {v_tag = strstr(extra_buffer, "<v>");if (v_tag) {//printf("<v>  ");value_start = v_tag + 3;char *v_end = strstr(v_tag, "</v>");if (v_end) {*v_end = '\0';strncpy(temp_value, value_start, MAX_CELL_CONTENT - 1);temp_value[MAX_CELL_CONTENT - 1] = '\0';} else {strncpy(temp_value, value_start, MAX_CELL_CONTENT - 1);temp_value[MAX_CELL_CONTENT - 1] = '\0';while (fgets(extra_buffer, MAX_LINE_LENGTH, file) && !strstr(extra_buffer, "</v>")) {// 继续读取}}cell_has_value = 1;extra_found = 1;break;}}if (!extra_found) {fseek(file, current_file_pos, SEEK_SET);}}}// 自闭合标签一定是空单元格if (is_self_closing || !cell_has_value) {is_empty = 1;temp_value[0] = '\0';}if (is_cell_in_range(current_row, current_cell_col, range)) {add_cell_result(current_row, current_cell_col, temp_value, is_empty);}}pos += 3;
}else if (strncmp(pos, "</c>", 4) == 0) {// 单元格结束current_col = '\0';pos += 4;}else {pos++;}}}
}/*** 添加单元格结果到结果数组*/
void add_cell_result(int row, char col, const char *value, int is_empty) {// 扩展结果数组if (result_count >= result_capacity) {result_capacity *= 2;results = (CellData *)realloc(results, result_capacity * sizeof(CellData));if (!results) {fprintf(stderr, "内存分配失败\n");return;}}results[result_count].row = row;results[result_count].col = col;strncpy(results[result_count].value, value, MAX_CELL_CONTENT - 1);results[result_count].value[MAX_CELL_CONTENT - 1] = '\0';results[result_count].is_empty = is_empty;result_count++;
}/*** 检查单元格是否在用户指定范围内*/
int is_cell_in_range(int row, char col, ParseRange range) {if (row < range.start_row || row > range.end_row) return 0;if (col < range.start_col || col > range.end_col) return 0;return 1;
}/*** 比较两个行列坐标* @return -1: row1<col1 < row2<col2, 0: 相等, 1: row1<col1 > row2<col2*/
int compare_row_col(int row1, char col1, int row2, char col2) {if (row1 != row2) return (row1 < row2) ? -1 : 1;if (col1 != col2) return (col1 < col2) ? -1 : 1;return 0;
}/*** 释放结果内存*/
void free_results() {if (results) {free(results);results = NULL;}result_count = 0;result_capacity = 0;
}/*** 打印解析结果*/
void print_results() {printf("解析结果:\n");for (int i = 0; i < result_count; i++) {if (results[i].is_empty) {printf("单元格 %c%d: (空)\n", results[i].col, results[i].row);} else {printf("单元格 %c%d: %s\n", results[i].col, results[i].row, results[i].value);}}
}/*** 以Excel的A1表示法打印解析范围* 例如:A1:H7* @param range 要打印的解析范围*/
void print_parse_range(ParseRange range) {printf("解析范围: %c%d:%c%d\n", range.start_col, range.start_row,range.end_col, range.end_row);
}
// 使用示例
int main() {ParseRange range = {6, 18, 'B', 'H'}; // 解析第3-5行,B-H列const char *filename = "sheet.xml";  // XML文件路径print_parse_range(range);  // 输出: 解析范围if (parse_sheet_xml(filename, range) == 0) {print_results();} else {printf("解析失败\n");}free_results();return 0;
}

测试用例如下

<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<sheetViews>
<sheetView showGridLines="false" workbookViewId="0"/>
</sheetViews>
<cols>
<col min="2" max="2" width="30.7109375" customWidth="true"/>
<col min="4" max="4" width="12.11328125" customWidth="true"/>
<col min="5" max="5" width="2.7109375" customWidth="true"/>
<col min="6" max="6" width="15.7109375" customWidth="true"/>
<col min="7" max="7" width="2.7109375" customWidth="true"/>
<col min="8" max="8" width="16.11328125" customWidth="true"/>
</cols>
<sheetData>
<row r="3">
<c r="B3" s="1" t="s">
<v>0</v>
</c>
<c r="D3" s="4" t="s">
<v>3</v>
</c>
<c r="F3" s="5" t="s">
<v>4</v>
</c>
<c r="H3" s="6" t="s">
<v>5</v>
</c>
</row>
<row r="4">
<c r="D4" s="4"/>
<c r="F4" s="5"/>
<c r="H4" s="6"/>
</row>
<row r="5">
<c r="B5" s="2" t="s">
<v>1</v>
</c>
<c r="D5" s="4"/>
<c r="F5" s="5"/>
<c r="H5" s="6"/>
</row>
<row r="6">
<c r="D6" s="4"/>
<c r="F6" s="5"/>
<c r="H6" s="6"/>
</row>
<row r="7">
<c r="B7" s="3" t="s">
<v>2</v>
</c>
<c r="D7" s="4"/>
<c r="F7" s="5"/>
<c r="H7" s="6"/>
</row>
<row r="13">
<c r="B13" s="7" t="s">
<v>6</v>
</c>
<c r="D13" s="13"/>
<c r="F13" s="19"/>
<c r="H13" s="25" t="s">
<v>12</v>
</c>
</row>
<row r="15">
<c r="B15" s="8" t="s">
<v>7</v>
</c>
<c r="D15" s="14"/>
<c r="F15" s="20"/>
<c r="H15" s="26" t="s">
<v>13</v>
</c>
</row>
<row r="17">
<c r="B17" s="9" t="s">
<v>8</v>
</c>
<c r="D17" s="15"/>
<c r="F17" s="21"/>
<c r="H17" s="27" t="s">
<v>14</v>
</c>
</row>
<row r="19">
<c r="B19" s="10" t="s">
<v>9</v>
</c>
<c r="D19" s="16"/>
<c r="F19" s="22"/>
<c r="H19" s="28" t="s">
<v>15</v>
</c>
</row>
<row r="21">
<c r="B21" s="11" t="s">
<v>10</v>
</c>
<c r="D21" s="17"/>
<c r="F21" s="23"/>
<c r="H21" s="29" t="s">
<v>16</v>
</c>
</row>
<row r="23">
<c r="B23" s="12" t="s">
<v>11</v>
</c>
<c r="D23" s="18"/>
<c r="F23" s="24"/>
<c r="H23" s="30" t="s">
<v>17</v>
</c>
</row>
</sheetData>
<mergeCells count="3">
<mergeCell ref="D3:D7"/>
<mergeCell ref="F3:F7"/>
<mergeCell ref="H3:H7"/>
</mergeCells>
</worksheet>
http://www.xdnf.cn/news/19873.html

相关文章:

  • 【数学建模学习笔记】时间序列分析:ARIMA
  • Scikit-learn从入门到实践:Scikit-learn入门-安装与基础操作
  • Qwen3-Reranker-0.6B 模型结构
  • Shell脚本一键监控平台到期时间并钉钉告警推送指定人
  • 自动化基本技术原理
  • 嵌入式解谜日志-网络编程
  • Kafka面试精讲 Day 5:Broker集群管理与协调机制
  • 基于SQLite的智能图片压缩存储系统:代码解析与实战应用
  • QuickUp-Ubuntu
  • FPGA AD7606串行驱动与并行驱动
  • 【Flask + Vue3 前后端分离管理系统】
  • 友思特案例 | 食品行业视觉检测案例集锦(三)
  • 利用 Python 获取微店商品关键词搜索 API 接口数据的实战指南
  • 利用飞算Java打造电商系统核心功能模块的设计与实现
  • 硬件开发(1)—单片机(1)
  • atomic常用类方法
  • VR智慧楼宇技术:打造智能办公空间的卓越方案​
  • 深圳外贸峰会究竟藏着啥秘密?能让外贸人收获满满?
  • RHEL9源码编译MySQL8.0.40
  • 图像加密安全传输--设备端视频流加密,手机端视频流解密,使用ChaCha20-Poly1305 进行系统分析
  • 爬虫-----最全的爬虫库介绍(一篇文章让你成为爬虫大佬,爬你想爬)
  • windows系统离线安装Ollama、创建模型(不使用docker)、coze调用
  • Linux为什么不是RTOS
  • 【Vue】前端 vue2项目搭建入门级(一)
  • IoT Power软件 -- 每次开启强制升级解决方法
  • Pandas Python数据处理库:高效处理Excel/CSV数据,支持分组统计与Matplotlib可视化联动
  • 嵌入式C语言之链表冒泡排序
  • 【PlayWright】 自动化测试框架机制详解
  • Python应用——ffmpeg处理音视频的常见场景
  • GitLab,2025最新如何配置中的SSH key步骤