C++ 使用ODBC操作Excel文件

C++ 使用ODBC操作Excel文件

一、ODBC介绍

​ 开放数据库连接(Open Database Connectivity,ODBC)是为解决异构数据库间的数据共享而产生的,现已成为WOSA(The Windows Open System Architecture(Windows开放系统体系结构))的主要部分和基于Windows环境的一种数据库访问接口标准。ODBC 为异构数据库访问提供统一接口,允许应用程序以SQL 为数据存取标准,存取不同DBMS管理的数据;使应用程序直接操纵DB中的数据,免除随DB的改变而改变。用ODBC 可以访问各类计算机上的DB文件,甚至访问如Excel 表和ASCI I数据文件这类非数据库对象。

二、环境

Visual Studio 2015

三、项目搭建

创建有MFC环境的32位控制台程序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5i0Nkm1f-1648544714884)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1648539324315.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lNxCHKza-1648544714885)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1648539641855.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EADr3dvW-1648544714886)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1648539680414.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PFMVHo4L-1648544714886)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1648539788824.png)]

提示: 32位控制台程序只是用于本地测试,程序代码还可以移植到其它有MFC环境的程序中。

四、编码

0)移除默认生成文件

将默认生成的代码文件移除,看着乱

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gAwxKvf7-1648544714887)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1648540104890.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kDJvL5hk-1648544714887)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1648540145116.png)]

1)excel_util.h

操作excel工具类的头文件

#include <iostream>
#include<stdlib.h>
#include <vector>
#include <map>
#include <afxdb.h>
#include <afx.h>
using namespace std;

CString get_columns_str(CString columns[], int columns_num);
CString get_create_sheet_sql(CString sheetName, CString columns[], CString columns_type[], int columns_num);
void WriteToExcel(CString filePath, CString sheetName, CString columns[], CString columns_type[], int columns_num, vector<vector<CString>> dataVec);
vector<vector<CString>> ReadFromExcel(CString filePath, CString sheetName, CString columns[], int columns_num);
2)excel_util.cpp

编写操作excel的工具类代码

#include "excel_util.h"

/*
获取sheet页的各列列名,返回格式如:[列1,列2,列3,···]
@param columns		sheet的各列名的数组
@param columns_num	sheet的列数
@author				lds
*/
CString get_columns_str(CString columns[], int columns_num)
{
	//printf("-----------------get_columns_str start ---------------\n");
	CString columns_str = "";  // 拼接的表头字符串,如:aa,bb,cc,dd
	for (size_t i = 0; i < columns_num; i++)
	{
		if (i == 0) {
			columns_str = columns_str + columns[i];
		}
		else
		{
			columns_str = columns_str + "," + columns[i];
		}
	}
	//printf("columns_str:[%s]", columns_str);
	//printf("-----------------get_columns_str end ---------------\n");
	return columns_str;
}

/*
获取创建excel的sheet页的sql语句
@param database		odbc数据源连接对象
@param sheetName	创建的sheet页名称
@param columns		sheet页的每列首行标题
@param columns_type	sheet页的每列单元格的类型
@param columns_num	sheet页的总列数
@return sql			创建excel的sheet页的sql语句
@author				lds
*/
CString get_create_sheet_sql(CString sheetName, CString columns[], CString columns_type[], int columns_num)
{
	//printf("----------------------get_create_sheet_sql start ------------------------\n");
	//创建表结构
	CString sql = "CREATE TABLE " + sheetName + "(";
	for (size_t i = 0; i < columns_num; i++)
	{
		if (i == 0) {
			sql = sql + columns[i] + " " + columns_type[i];
		}
		else
		{
			sql = sql + "," + columns[i] + " " + columns_type[i];
		}
	}
	sql = sql + ")";
	//printf("----------------------get_create_sheet_sql end ------------------------\n");
	return sql;

}

/*
使用ODBC往Excel里写数据。
@param filePath		创建excel文件的地址			如:C:/demo.xlsx;文件不存在时会自动生成
@param sheetName	创建的sheet页名称			如:sheet1
@param columns		sheet页的每列首行标题
如:CString columns[11] = { "序号","任务名称","物料识别码","对应的流程","设计人" ,"状态","当前任务信息","当前节点责任人","开始时间" ,"持续时间(天)","完成时间" };
@param columns_type	sheet页的每列单元格的类型
如:CString columns_type[11] = { "NUMBER","TEXT","TEXT","TEXT","TEXT" ,"TEXT","TEXT","TEXT","TEXT" ,"NUMBER","TEXT" };
@param columns_num	sheet页的总列数				如:int:11
@param data			待写入的所有数据
如:vector<vector<CString>> data = {
{ "1","名称1","识别码1" ,"流程1" ,"设计人1" ,"状态1" ,"任务信息1" ,"责任人1" ,"2022-03-10 16:38:07" ,"0" ,"2022-03-10 16:38:11" },
{ "2","名称2","识别码2" ,"流程2" ,"设计人2" ,"状态2" ,"任务信息2" ,"责任人2" ,"2022-03-10 16:38:07" ,"0" ,"2022-03-10 16:38:11" },
{ "3","名称3","识别码3" ,"流程3" ,"设计人3" ,"状态3" ,"任务信息3" ,"责任人3" ,"2022-03-10 16:38:07" ,"0" ,"2022-03-10 16:38:11" },
{ "4","名称4","识别码4" ,"流程4" ,"设计人4" ,"状态4" ,"任务信息4" ,"责任人4" ,"2022-03-10 16:38:07" ,"0" ,"2022-03-10 16:38:11" }
};
@author				lds
*/
void WriteToExcel(CString filePath, CString sheetName, CString columns[], CString columns_type[], int columns_num, vector<vector<CString>> dataVec)
{
	printf("-------------------------WriteToExcel start----------------------------\n");
	CDatabase database;
	CString sDriver = "Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)";//Excel安装驱动
	CString sql; //db执行语句
				 //创建进行存取的字符串
	sql.Format("DRIVER={%s};DSN='Excel nFiles';READONLY=FALSE;DBQ=%s", sDriver, filePath);;
	//创建数据库(即Excel表格文件)
	try
	{
		if (database.OpenEx(sql, CDatabase::noOdbcDialog))
		{
			CRecordset recset(&database);
			// 查询sheet页是否存在,不存在则创建
			sql = "select * from " + sheetName;
			// cout << "查询sheet页是否存在sql:[" << sql << "]" << endl;
			try
			{
				recset.Open(CRecordset::forwardOnly, sql, CRecordset::readOnly);
				// printf("----sheet页已经存在!\n");
			}
			catch (...)
			{
				// printf("----sheet页不存在!创建sheet页!\n");
				sql = get_create_sheet_sql(sheetName, columns, columns_type, columns_num);
				// cout << "create sheet sql:[" << sql << "]" << endl;
				try
				{
					database.ExecuteSQL(sql);
					// printf("create [%s] sheet success!\n", sheetName);
				}
				catch (...)
				{
					printf("create [%s] sheet is fail!\n", sheetName);
				}
			}

			printf("Startting To Write Data In Excel...\n");
			// 获取各列名
			CString columns_str = get_columns_str(columns, columns_num);
			// 循环插入数据
			int success_count = 0;  // 插入数据成功个数
			int error_count = 0;  // 插入数据成功个数
			for (int i = 0; i < dataVec.size(); i++)
			{
				sql = "insert into " + sheetName + "(" + columns_str + ")VALUES(";
				vector<CString> rowData = dataVec[i]; // 每行数据
				for (int j = 0; j < rowData.size(); j++)
				{
					if (j == 0)
					{
						sql = sql + "\'" + rowData[j] + "\'";
					}
					else
					{
						sql = sql + "," + "\'" + rowData[j] + "\'";
					}
				}
				sql = sql + ")";
				// cout << "insert sql:[" << sql << "]" << endl;
				// 逐行写入
				try {
					database.ExecuteSQL(sql);
					success_count = success_count + 1;
				}
				catch (...)
				{
					printf("insert data is fail!SQL is [%s]\n", sql);
					error_count = error_count + 1;
				}

			}
			printf("All Data Write To Excel complete!Success count:%d;Error count:%d。\n", success_count, error_count);
		}
	}
	catch (...) {
		printf("Register ODBC Driver Is Fail!\n");
	}
	//关闭数据库
	database.Close();
	printf("-------------------------WriteToExcel end----------------------------\n");
}

/*
使用ODBC读Excel表格数据
@param filePath		创建excel文件的地址			如:C:/demo.xlsx;文件不存在时会自动生成
@param sheetName	创建的sheet页名称			如:sheet1
@param columns		sheet页的每列首行标题
如:CString columns[11] = { "序号","任务名称","物料识别码","对应的流程","设计人" ,"状态","当前任务信息","当前节点责任人","开始时间" ,"持续时间(天)","完成时间" };
@param columns_num	sheet页的总列数				如:int:11
@return				sheet页所有行数据
@author				lds
*/
vector<vector<CString>> ReadFromExcel(CString filePath, CString sheetName, CString columns[], int columns_num)
{
	printf("-------------------------------ReadFromExcel start------------------------------\n");
	//获取查询结果
	vector<vector<CString>> rows;   // sheet页所有行数据(不包含首行-标题行)
	CDatabase database;
	CString sql;
	CString driver = "Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)";
	printf("driver:[%s]", driver);
	//创建进行存储的字符串
	CString ssql;
	ssql.Format("ODBC;DRIVER={%s};DSN='Excel nFiles';DBQ=%s", driver, filePath);
	//打开数据库
	try
	{
		if (database.Open(NULL, false, false, ssql))
		{
			/*
			要实现对结果集的数据操作,就要用到CRecordSet类。
			CRecordSet类定义了从数据库接收或者发送数据到数据库的成员变量,CRecordSet类定义的记录集可以是表的所有列,也可以是其中的一列,这是由SQL语句决定的。
			CRecordSet类的成员变量m_hstmt代表了定义该记录集的SQL语句句柄,m_nFields成员变量保存了记录集中字段的个数,m_nParams成员变量保存了记录集所使用的参数个数
			*/
			CRecordset recset(&database);
			//设置读取的查询语句
			CString columns_str = get_columns_str(columns, columns_num);
			sql = "SELECT " + columns_str + " FROM " + sheetName;
			printf("执行的查询sql是:[%s]\n", sql);
			//执行查询语句
			try
			{
				recset.Open(CRecordset::forwardOnly, sql, CRecordset::readOnly);
				while (!recset.IsEOF())
				{
					vector<CString> row;
					for (int i = 0; i < columns_num; i++)
					{
						CString item;
						recset.GetFieldValue(columns[i], item);
						printf("%s:%s;", columns[i], item);
						row.push_back(item);
					}
					printf("\n");
					rows.push_back(row);
					//移动到下一行
					recset.MoveNext();
				}
			}
			catch (...)
			{
				printf("执行查询sheet页数据失败!\n");
				printf("可能原因1:sql的列标题与实际的excel列标题对应不上!sql的列标题是: [%s],请检查excel的列标题!\n", columns_str);
				printf("可能原因2:执行查询sheet页数据的sql有问题,sql是: [%s]\n", sql);

			}
			database.Close();
			printf("Read Excel is complete!Results count is %d\n", rows.size());
		}
	}
	catch (...)
	{
		printf("Register ODBC Driver Is Fail!\n");
	}
	printf("-------------------------------ReadFromExcel end------------------------------\n");
	return rows;
}
3)main.cpp

程序入口,进行测试。

#include "excel_util.h"

int main()
{
	CString columns[11] = { "序号","任务名称","物料识别码","对应的流程","设计人" ,"状态","当前任务信息","当前节点责任人","开始时间" ,"持续时间(天)","完成时间" };
	CString columns_type[11] = { "NUMBER","TEXT","TEXT","TEXT","TEXT" ,"TEXT","TEXT","TEXT","TEXT" ,"NUMBER","TEXT" };
	vector<vector<CString>> data = {
		{ "1","名称1","识别码1" ,"流程1" ,"设计人1" ,"状态1" ,"任务信息1" ,"责任人1" ,"2022-03-10 16:38:07" ,"0" ,"2022-03-10 16:38:11" },
		{ "2","名称2","识别码2" ,"流程2" ,"设计人2" ,"状态2" ,"任务信息2" ,"责任人2" ,"2022-03-10 16:38:07" ,"0" ,"2022-03-10 16:38:11" },
		{ "3","名称3","识别码3" ,"流程3" ,"设计人3" ,"状态3" ,"任务信息3" ,"责任人3" ,"2022-03-10 16:38:07" ,"0" ,"2022-03-10 16:38:11" },
		{ "4","名称4","识别码4" ,"流程4" ,"设计人4" ,"状态4" ,"任务信息4" ,"责任人4" ,"2022-03-10 16:38:07" ,"0" ,"2022-03-10 16:38:11" }
	};
	// cout << size(columns) << endl;
	CString filePath = "E:/writeDemo1111.xlsx";
	CString sheetName = "mysheet1";
	int columns_num = size(columns);
	WriteToExcel(filePath, sheetName, columns, columns_type, columns_num, data);
	vector<vector<CString>> results = ReadFromExcel(filePath, sheetName, columns, columns_num);
	system("pause");
	return 0;
}
4、最终项目结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0eBv1UNh-1648544714889)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1648542163975.png)]

五、测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cIhoM0gc-1648544714890)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1648542964113.png)]

在这里插入图片描述

本地可以看到excel成功生成了,并且数据也是正确写入了

在这里插入图片描述
在这里插入图片描述

六、可能异常及解决

异常一:预编译头未使用

在这里插入图片描述

解决:

在这里插入图片描述

异常二:参数类型转换异常

在这里插入图片描述

解决:

在这里插入图片描述

异常三:未知编码格式错误

在这里插入图片描述

在这里插入图片描述

解决:

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dnldnbKp-1648544714894)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1648541283248.png)]

[外链图片转存中...(img-JLfhC1SN-1648544714892)]

可以使用ODBC(Open Database Connectivity)来连接Excel文件,然后使用C++代码来读写文件。 以下是使用ODBC连接Excel文件的步骤: 1. 安装Excel ODBC驱动程序 首先需要安装Excel ODBC驱动程序。在Windows系统中,可以在“控制面板” -> “管理员工具” -> “ODBC数据源(64位)”或“ODBC数据源(32位)”中找到该驱动程序。 2. 创建ODBC数据源 打开ODBC数据源窗口,单击“系统DSN”选项卡,然后单击“添加”按钮。在“创建新数据源”对话框中,选择Excel驱动程序,然后单击“完成”按钮。在“ODBC Microsoft Excel Setup”对话框中,指定Excel文件的名称和路径,然后单击“确定”按钮。这样就创建了一个ODBC数据源。 3. 使用ODBC API连接Excel文件C++代码中,可以使用ODBC API连接到Excel文件。以下是一个示例程序,它使用ODBC API连接到Excel文件并将数据写入其中: ```c++ #include <iostream> #include <windows.h> #include <sql.h> #include <sqlext.h> int main() { // 初始化ODBC环境 SQLHENV env; SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0); // 连接到Excel文件 SQLHDBC dbc; SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc); SQLCHAR* connStr = (SQLCHAR*)"DRIVER={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DBQ=C:\\test.xlsx;"; SQLRETURN rc = SQLDriverConnect(dbc, NULL, connStr, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE); if (SQL_SUCCEEDED(rc)) { // 执行SQL语句 SQLHSTMT stmt; SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt); rc = SQLExecDirect(stmt, (SQLCHAR*)"CREATE TABLE Sheet1 (ID INT, Name VARCHAR(50))", SQL_NTS); if (SQL_SUCCEEDED(rc)) { // 插入数据 SQLCHAR* insertQuery = (SQLCHAR*)"INSERT INTO Sheet1 (ID, Name) VALUES (1, 'John')"; rc = SQLExecDirect(stmt, insertQuery, SQL_NTS); } // 释放资源 SQLFreeHandle(SQL_HANDLE_STMT, stmt); } SQLDisconnect(dbc); SQLFreeHandle(SQL_HANDLE_DBC, dbc); SQLFreeHandle(SQL_HANDLE_ENV, env); return 0; } ``` 此代码演示了如何使用ODBC API连接到Excel文件并创建一个名为“Sheet1”的表,然后向其中插入一个记录。你可以根据自己的需求修改此代码来读取或更新Excel文件中的数据。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值