【史上最全】五种C语言操作Excel文件的方法全面深入指南

在C语言中操作Excel文件是一项常见且重要的任务,特别是在数据处理和科学计算领域。尽管C语言本身并不直接支持Excel文件的读写操作,但通过多种方法和技术,我们仍然可以高效地完成这项任务。本文将详细介绍几种常用的C语言操作Excel文件的方法,包括使用CSV格式、调用COM接口、使用第三方库等,并对每种方法的优缺点进行深入分析。
在这里插入图片描述

1. 使用CSV格式

CSV(Comma-Separated Values)是一种简单的文本格式,用于存储表格数据。通过将Excel文件另存为CSV格式,我们可以使用标准的文件操作函数来读写Excel数据。

1.1 写操作
#include <stdio.h>

void writeExcel() {
    char chy[4] = {'x', 'a', 'h', 'w'};
    int data[4] = {1, 3, 6, 9};
    int i;
    FILE *fp = NULL;
    fp = fopen("test.csv", "w");
    for (i = 0; i < 4; i++) {
        fprintf(fp, "%c,%d\n", chy[i], data[i]);
    }
    fclose(fp);
}

int main() {
    writeExcel();
    return 0;
}
1.2 读操作
#include <stdio.h>

int main() {
    FILE *fp;
    char filename[40];
    int i, j;
    float da[6][5] = {0};
    printf("输入文件名: ");
    scanf("%s", filename);
    fp = fopen(filename, "r");
    for (i = 0; i < 6; i++) {
        for (j = 0; j < 5; j++) {
            fscanf(fp, "%f,", &da[i][j]);
        }
    }
    for (i = 0; i < 6; i++) {
        for (j = 0; j < 5; j++) {
            printf("%f\t", da[i][j]);
        }
        printf("\n");
    }
    fclose(fp);
    return 0;
}

优点

  • 简单易用:不需要额外的库或工具,只需使用标准的文件操作函数。
  • 兼容性强:可以在多种平台上使用,不受操作系统的限制。

缺点

  • 功能有限:不支持复杂的Excel功能,如公式、图表等。
  • 数据格式单一:无法保留原始Excel文件的样式和格式。

适用场景

  • 简单数据导入导出:适用于只需要导入导出简单数据的场景。
  • 数据预处理:在数据预处理阶段,可以将Excel数据转换为CSV格式,以便后续处理。
2. 调用COM接口

COM(Component Object Model)接口允许C语言程序与Excel应用程序进行交互。通过COM接口,我们可以直接操作Excel文件,执行复杂的操作。

2.1 示例代码
#include <windows.h>
#include <ole2.h>
#include <stdio.h>

void writeExcel(const char *filename) {
    HRESULT hr;
    CLSID clsid;
    IDispatch *pXlApp = NULL;
    IDispatch *pXlBooks = NULL;
    IDispatch *pXlBook = NULL;
    IDispatch *pXlSheet = NULL;

    // 初始化COM库
    CoInitialize(NULL);

    // 获取Excel应用程序的CLSID
    CLSIDFromProgID(L"Excel.Application", &clsid);

    // 创建Excel应用程序对象
    hr = CoCreateInstance(&clsid, NULL, CLSCTX_LOCAL_SERVER, &IID_IDispatch, (void **)&pXlApp);
    if (FAILED(hr)) {
        printf("Failed to create Excel application\n");
        return;
    }

    // 获取工作簿集合
    OLECHAR *szBooks = L"Workbooks";
    DISPID dispidBooks;
    hr = pXlApp->lpVtbl->GetIDsOfNames(pXlApp, &IID_NULL, &szBooks, 1, LOCALE_USER_DEFAULT, &dispidBooks);
    if (FAILED(hr)) {
        printf("Failed to get Workbooks ID\n");
        return;
    }

    DISPPARAMS paramsNoArgs = {NULL, NULL, 0, 0};
    VARIANT resultBooks;
    hr = pXlApp->lpVtbl->Invoke(pXlApp, dispidBooks, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &paramsNoArgs, &resultBooks, NULL, NULL);
    if (FAILED(hr)) {
        printf("Failed to get Workbooks\n");
        return;
    }
    pXlBooks = resultBooks.pdispVal;

    // 创建一个新的工作簿
    OLECHAR *szAdd = L"Add";
    DISPID dispidAdd;
    hr = pXlBooks->lpVtbl->GetIDsOfNames(pXlBooks, &IID_NULL, &szAdd, 1, LOCALE_USER_DEFAULT, &dispidAdd);
    if (FAILED(hr)) {
        printf("Failed to get Add ID\n");
        return;
    }

    VARIANT resultBook;
    hr = pXlBooks->lpVtbl->Invoke(pXlBooks, dispidAdd, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &paramsNoArgs, &resultBook, NULL, NULL);
    if (FAILED(hr)) {
        printf("Failed to add Workbook\n");
        return;
    }
    pXlBook = resultBook.pdispVal;

    // 获取第一个工作表
    OLECHAR *szSheets = L"Worksheets";
    DISPID dispidSheets;
    hr = pXlBook->lpVtbl->GetIDsOfNames(pXlBook, &IID_NULL, &szSheets, 1, LOCALE_USER_DEFAULT, &dispidSheets);
    if (FAILED(hr)) {
        printf("Failed to get Worksheets ID\n");
        return;
    }

    VARIANT resultSheets;
    hr = pXlBook->lpVtbl->Invoke(pXlBook, dispidSheets, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &paramsNoArgs, &resultSheets, NULL, NULL);
    if (FAILED(hr)) {
        printf("Failed to get Worksheets\n");
        return;
    }
    IDispatch *pXlSheets = resultSheets.pdispVal;

    OLECHAR *szItem = L"Item";
    DISPID dispidItem;
    hr = pXlSheets->lpVtbl->GetIDsOfNames(pXlSheets, &IID_NULL, &szItem, 1, LOCALE_USER_DEFAULT, &dispidItem);
    if (FAILED(hr)) {
        printf("Failed to get Item ID\n");
        return;
    }

    VARIANT index;
    index.vt = VT_I4;
    index.lVal = 1;
    DISPPARAMS paramsItem = {&index, NULL, 1, 0};
    VARIANT resultSheet;
    hr = pXlSheets->lpVtbl->Invoke(pXlSheets, dispidItem, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &paramsItem, &resultSheet, NULL, NULL);
    if (FAILED(hr)) {
        printf("Failed to get Worksheet\n");
        return;
    }
    pXlSheet = resultSheet.pdispVal;

    // 写入数据
    OLECHAR *szCells = L"Cells";
    DISPID dispidCells;
    hr = pXlSheet->lpVtbl->GetIDsOfNames(pXlSheet, &IID_NULL, &szCells, 1, LOCALE_USER_DEFAULT, &dispidCells);
    if (FAILED(hr)) {
        printf("Failed to get Cells ID\n");
        return;
    }

    VARIANT row, col;
    row.vt = VT_I4;
    col.vt = VT_I4;
    row.lVal = 1;
    col.lVal = 1;
    DISPPARAMS paramsCell = {&row, &col, 2, 0};
    VARIANT resultCell;
    hr = pXlSheet->lpVtbl->Invoke(pXlSheet, dispidCells, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &paramsCell, &resultCell, NULL, NULL);
    if (FAILED(hr)) {
        printf("Failed to get Cell\n");
        return;
    }

    IDispatch *pXlCell = resultCell.pdispVal;
    OLECHAR *szValue = L"Value";
    DISPID dispidValue;
    hr = pXlCell->lpVtbl->GetIDsOfNames(pXlCell, &IID_NULL, &szValue, 1, LOCALE_USER_DEFAULT, &dispidValue);
    if (FAILED(hr)) {
        printf("Failed to get Value ID\n");
        return;
    }

    VARIANT value;
    value.vt = VT_BSTR;
    value.bstrVal = SysAllocString(L"Hello, World!");
    DISPPARAMS paramsValue = {&value, NULL, 1, 0};
    hr = pXlCell->lpVtbl->Invoke(pXlCell, dispidValue, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &paramsValue, NULL, NULL, NULL);
    if (FAILED(hr)) {
        printf("Failed to set Cell Value\n");
        return;
    }

    // 保存并关闭
    OLECHAR *szSaveAs = L"SaveAs";
    DISPID dispidSaveAs;
    hr = pXlBook->lpVtbl->GetIDsOfNames(pXlBook, &IID_NULL, &szSaveAs, 1, LOCALE_USER_DEFAULT, &dispidSaveAs);
    if (FAILED(hr)) {
        printf("Failed to get SaveAs ID\n");
        return;
    }

    VARIANT filename;
    filename.vt = VT_BSTR;
    filename.bstrVal = SysAllocString(L"output.xlsx");
    DISPPARAMS paramsSaveAs = {&filename, NULL, 1, 0};
    hr = pXlBook->lpVtbl->Invoke(pXlBook, dispidSaveAs, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &paramsSaveAs, NULL, NULL, NULL);
    if (FAILED(hr)) {
        printf("Failed to save Workbook\n");
        return;
    }

    OLECHAR *szQuit = L"Quit";
    DISPID dispidQuit;
    hr = pXlApp->lpVtbl->GetIDsOfNames(pXlApp, &IID_NULL, &szQuit, 1, LOCALE_USER_DEFAULT, &dispidQuit);
    if (FAILED(hr)) {
        printf("Failed to get Quit ID\n");
        return;
    }

    hr = pXlApp->lpVtbl->Invoke(pXlApp, dispidQuit, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &paramsNoArgs, NULL, NULL, NULL);
    if (FAILED(hr)) {
        printf("Failed to quit Excel application\n");
        return;
    }

    // 释放资源
    VariantClear(&value);
    SysFreeString(szValue);
    VariantClear(&row);
    VariantClear(&col);
    VariantClear(&resultCell);
    VariantClear(&resultSheet);
    VariantClear(&resultSheets);
    VariantClear(&resultBook);
    VariantClear(&resultBooks);
    VariantClear(&index);
    VariantClear(&paramsCell);
    VariantClear(&paramsItem);
    VariantClear(&paramsValue);
    VariantClear(&paramsNoArgs);

    pXlCell->lpVtbl->Release(pXlCell);
    pXlSheet->lpVtbl->Release(pXlSheet);
    pXlSheets->lpVtbl->Release(pXlSheets);
    pXlBook->lpVtbl->Release(pXlBook);
    pXlBooks->lpVtbl->Release(pXlBooks);
    pXlApp->lpVtbl->Release(pXlApp);

    CoUninitialize();
}

int main() {
    writeExcel("output.xlsx");
    return 0;
}

优点

  • 功能强大:支持复杂的Excel功能,如公式、图表等。
  • 直接操作:可以直接操作Excel应用程序,实现高级功能。

缺点

  • 代码复杂:需要处理大量的COM接口调用,代码复杂度较高。
  • 依赖性强:依赖于Windows操作系统和Excel应用程序,不适用于其他平台。

适用场景

  • 高级操作:需要直接操作Excel应用程序的场景,如生成图表、执行宏等。
  • 复杂需求:需要实现复杂功能,如数据验证、条件格式等。
3. 使用第三方库

使用第三方库是另一种高效的操作Excel文件的方法。这些库提供了丰富的API,简化了操作步骤。

3.1 使用libxlsxwriter库

libxlsxwriter 是一个开源库,用于创建Excel XLSX文件。

3.1.1 安装
sudo apt-get install libxlsxwriter-dev
3.1.2 示例代码
#include "xlsxwriter.h"

void writeExcel(const char *filename) {
    lxw_workbook *workbook = workbook_new(filename);
    lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);

    // 写入数据
    worksheet_write_string(worksheet, 0, 0, "Hello, World!", NULL);
    worksheet_write_number(worksheet, 1, 0, 123.45, NULL);

    // 关闭工作簿
    workbook_close(workbook);
}

int main() {
    writeExcel("output.xlsx");
    return 0;
}
3.2 使用libxls库

libxls 是一个用于读取Excel XLS文件的库。

3.2.1 安装
sudo apt-get install libxls-dev
3.2.2 示例代码
#include <stdio.h>
#include <libxls/xls.h>

void readExcel(const char *filename) {
    xlsWorkBook *workbook = xls_open(filename, "UTF-8");
    if (workbook == NULL) {
        printf("Failed to open the file.\n");
        return;
    }

    for (int i = 0; i < workbook->sheets.count; ++i) {
        xlsWorkSheet *sheet = xls_getWorkSheet(workbook, i);
        xls_parseWorkSheet(sheet);
        for (int row = 0; row <= sheet->rows.lastrow; ++row) {
            for (int col = 0; col <= sheet->rows.lastcol; ++col) {
                xlsCell *cell = xls_cell(sheet, row, col);
                if (cell && cell->str) {
                    printf("Row %d, Col %d: %s\n", row, col, cell->str);
                }
            }
        }
        xls_close_WS(sheet);
    }
    xls_close_WB(workbook);
}

int main() {
    readExcel("input.xls");
    return 0;
}
3.3 使用libxlsxreader库

libxlsxreader 是一个用于读取Excel XLSX文件的库。

3.3.1 安装
sudo apt-get install libxlsxreader-dev
3.3.2 示例代码
#include <stdio.h>
#include <xlsxreader/xlsxreader.h>

void readExcel(const char *filename) {
    lxw_reader *reader = reader_new(filename);
    if (reader == NULL) {
        printf("Failed to open the file.\n");
        return;
    }

    int num_sheets = reader_get_num_sheets(reader);
    printf("Number of sheets: %d\n", num_sheets);

    for (int i = 0; i < num_sheets; i++) {
        lxw_worksheet *worksheet = reader_get_worksheet(reader, i);
        int rows = worksheet_get_row_count(worksheet);
        int cols = worksheet_get_col_count(worksheet);
        printf("Sheet %d: %d rows, %d columns\n", i + 1, rows, cols);

        for (int row = 0; row < rows; row++) {
            for (int col = 0; col < cols; col++) {
                lxw_cell *cell = worksheet_get_cell(worksheet, row, col);
                if (cell) {
                    const char *value = cell_get_value(cell);
                    printf("Row %d, Column %d: %s\n", row, col, value);
                }
            }
        }
    }

    reader_free(reader);
}

int main() {
    readExcel("input.xlsx");
    return 0;
}

优点

  • API丰富:提供了丰富的API,简化了操作步骤。
  • 支持多种格式:支持多种Excel文件格式,包括XLS和XLSX。

缺点

  • 依赖库:需要安装额外的库,增加了项目的依赖。
  • 兼容性问题:可能存在兼容性问题,特别是对于某些特殊的Excel文件。

适用场景

  • 多格式支持:需要处理多种Excel文件格式的场景。
  • 高效读写:需要高效读写大量数据的场景。
4. 使用API接口

除了上述方法,还可以通过API接口(如Microsoft Excel API、Google Sheets API等)来操作Excel文件。

4.1 使用Microsoft Excel API
4.1.1 示例代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>

#define ACCESS_TOKEN "YOUR_ACCESS_TOKEN"
#define EXCEL_FILE_ID "YOUR_EXCEL_FILE_ID"

void readExcel() {
    CURL *curl;
    CURLcode res;
    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();

    if (curl) {
        char url[256];
        snprintf(url, sizeof(url), "https://graph.microsoft.com/v1.0/me/drive/items/%s/workbook/worksheets", EXCEL_FILE_ID);
        struct curl_slist *headers = NULL;
        headers = curl_slist_append(headers, "Authorization: Bearer " ACCESS_TOKEN);
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        res = curl_easy_perform(curl);

        if (res != CURLE_OK) {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        }

        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }

    curl_global_cleanup();
}

int main() {
    readExcel();
    return 0;
}

优点

  • 远程操作:支持远程操作Excel文件,适用于分布式系统。
  • 功能丰富:提供丰富的接口,可以实现复杂的功能。

缺点

  • 网络依赖:需要网络连接,依赖于API服务的可用性。
  • 开发难度:需要进行身份验证和授权,增加了开发难度。

适用场景

  • 远程操作:需要远程操作Excel文件的场景。
  • 分布式系统:分布式系统中需要协同操作Excel文件的场景。
5. 直接解析Excel文件格式

直接解析Excel文件格式是一项复杂的任务,因为Excel文件格式(尤其是XLSX)非常复杂,并且包含了大量的元数据和压缩数据。不过,理解基本的文件格式对于某些特殊需求可能是必要的。

5.1 解析XLS文件

XLS文件格式是二进制格式,解析起来相对复杂。一般来说,不建议手动解析XLS文件,除非有非常特殊的需求。

5.2 解析XLSX文件

XLSX文件实际上是一个压缩包,里面包含了多个XML文件。这些XML文件描述了工作表的数据、样式、关系等信息。

5.2.1 示例代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zip.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

void parseXLSX(const char *filename) {
    struct zip *z = zip_open(filename, 0, NULL);
    if (!z) {
        fprintf(stderr, "Failed to open zip file\n");
        return;
    }

    struct zip_stat st;
    zip_stat_init(&st);
    zip_stat(z, "xl/worksheets/sheet1.xml", 0, &st);

    struct zip_file *zf = zip_fopen(z, "xl/worksheets/sheet1.xml", 0);
    if (!zf) {
        fprintf(stderr, "Failed to open sheet1.xml\n");
        zip_close(z);
        return;
    }

    char *contents = malloc(st.size);
    zip_fread(zf, contents, st.size);
    zip_fclose(zf);

    xmlDocPtr doc = xmlReadMemory(contents, st.size, "sheet1.xml", NULL, 0);
    if (doc == NULL) {
        fprintf(stderr, "Failed to parse XML\n");
        free(contents);
        zip_close(z);
        return;
    }

    xmlNodePtr root = xmlDocGetRootElement(doc);
    if (root == NULL) {
        fprintf(stderr, "Empty document\n");
        xmlFreeDoc(doc);
        free(contents);
        zip_close(z);
        return;
    }

    xmlNodePtr node = root->children;
    while (node != NULL) {
        if (xmlStrcmp(node->name, (const xmlChar *)"sheetData") == 0) {
            xmlNodePtr rowNode = node->children;
            while (rowNode != NULL) {
                if (xmlStrcmp(rowNode->name, (const xmlChar *)"row") == 0) {
                    xmlNodePtr cellNode = rowNode->children;
                    while (cellNode != NULL) {
                        if (xmlStrcmp(cellNode->name, (const xmlChar *)"c") == 0) {
                            xmlChar *value = xmlGetProp(cellNode, (const xmlChar *)"v");
                            if (value) {
                                printf("%s\t", value);
                                xmlFree(value);
                            }
                        }
                        cellNode = cellNode->next;
                    }
                    printf("\n");
                }
                rowNode = rowNode->next;
            }
        }
        node = node->next;
    }

    xmlFreeDoc(doc);
    free(contents);
    zip_close(z);
}

int main() {
    parseXLSX("input.xlsx");
    return 0;
}

优点

  • 自主控制:不依赖于第三方库,完全自主控制。
  • 高度定制:可以处理复杂的文件格式和元数据。

缺点

  • 实现复杂:需要深入了解Excel文件格式,实现复杂。
  • 开发成本高:开发和维护成本高,不适合简单的项目需求。

适用场景

  • 特殊需求:需要高度定制的解析逻辑,适用于特殊需求。
  • 高性能要求:避免依赖第三方库的性能瓶颈,适用于高性能要求的场景。
6. 结论

通过上述介绍,我们可以看到C语言操作Excel文件有多种方法,每种方法都有其适用场景和优缺点。选择合适的方法取决于具体的项目需求和开发环境:

  • CSV格式:适用于简单的需求,不需要复杂的Excel功能。
  • COM接口:适用于需要直接操作Excel应用程序的场景,但依赖于Windows操作系统。
  • 第三方库:提供了丰富的API,简化了操作步骤,适用于大多数场景。
  • API接口:支持远程操作Excel文件,适用于分布式系统。
  • 直接解析Excel文件格式:适用于特殊需求,需要高度定制的解析逻辑。

希望本文能帮助你在C语言中高效地操作Excel文件,满足不同的开发需求。无论你是初学者还是经验丰富的开发者,都能找到适合自己的方法,提高开发效率和代码质量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值