C语言通过ODBC函数操作Access数据库(mdb和accdb格式)(char字符数组)

news2025/4/7 15:21:29

编译环境:Windows XP + Visual Studio 2010
数据库:Access 2010,accdb格式
本例程只使用char[]字符数组,不使用wchar_t[]字符数组,更适合C语言初学者。
如果读取字符串时,提供的字符数组空间小了,db_fetch会执行失败返回-1。
由于Windows系统设计原因,char[]字符数组只能存储GB2312编码的字符串,wchar_t[]字符数组只能存储UTF-16编码的字符串。如果是UTF-8编码的字符串(也用char[]数组存储),必须用MultiByteToWideChar函数转成UTF-16格式(用wchar_t[]数组存储,CodePage参数=CP_UTF8),再调用W版本的API函数操作数据库。

请注意,Visual Studio里面使用以_s结尾的函数时,凡是参数名是maxcount的,都要减1,否则程序会闪退。
_snprintf_s函数的正确用法:
_snprintf_s(str, sizeof(str), sizeof(str) - 1, "fmt"); // C语言专用
_snprintf_s(str, sizeof(str) - 1, "fmt"); // C++专用, 和上面的语句等效

如果系统是32位,请编译并运行32位版本的程序;如果系统是64位,请编译并运行64位版本的程序,否则无法成功连接数据库。

主代码(main.c):

#include <stdio.h>
#include "db.h"

void show_products()
{
	char *sql;
	char name[50];
	double price;
	int id, num;
	size_t namelen;
	Statement stmt;

	sql = "SELECT [产品ID], [产品名称], [单价], [库存量] FROM [产品] WHERE [产品ID] <= 4 ORDER BY [产品ID]";
	stmt = db_query(sql);
	if (stmt != NULL)
	{
		db_bind_int(stmt, 1, &id);
		db_bind_str(stmt, 2, name, sizeof(name), &namelen);
		db_bind_double(stmt, 3, &price);
		db_bind_int(stmt, 4, &num);
		while (db_fetch(stmt) == 1)
			printf("%d %s(len=%d) %.2lf %d\n", id, name, (int)namelen, price, num);
		db_free(stmt);
	}

	sql = "SELECT * FROM [产品] WHERE [产品名称] = '苏打水'";
	printf("exists: %d\n", db_has_records(sql));
}

void find_product_by_id(int id)
{
	char *sql;
	char name[50];
	Statement stmt;

	sql = "SELECT [产品名称], [单价], [库存量] FROM [产品] WHERE [产品ID] = ?";
	stmt = db_prepare(sql);
	if (stmt != NULL)
	{
		db_set_int(stmt, 1, &id);
		db_exec_stmt(stmt, 0);
		while (db_fetch(stmt) == 1)
		{
			printf("find %d: %s %.2f %d\n", id, db_get_str(stmt, 1, name, sizeof(name), NULL), db_get_float(stmt, 2), db_get_int(stmt, 3));
		}
		db_free(stmt);
	}
}

void find_product_by_name(const char *name)
{
	char *sql;
	Statement stmt;

	sql = "SELECT [产品ID], [单价], [库存量] FROM [产品] WHERE [产品名称] = ?";
	stmt = db_prepare(sql);
	if (stmt != NULL)
	{
		db_set_str(stmt, 1, name);
		db_exec_stmt(stmt, 0);
		while (db_fetch(stmt) == 1)
			printf("find %s: %d %.2lf %d\n", name, db_get_int(stmt, 1), db_get_double(stmt, 2), db_get_int(stmt, 3));
		db_free(stmt);
	}
}

void update_products()
{
	char *sql;
	double dval;
	int cnt;
	Statement stmt;

	sql = "UPDATE [产品] SET [库存量] = [库存量] + 1 WHERE [产品ID] = 1";
	db_exec(sql, &cnt);
	printf("update: cnt=%d\n", cnt);

	sql = "UPDATE [产品] SET [库存量] = [库存量] + 1 WHERE [产品名称] = ?";
	stmt = db_prepare(sql);
	if (stmt != NULL)
	{
		db_set_str(stmt, 1, "牛奶");
		db_exec_stmt(stmt, 0);
		cnt = db_affected_rows(stmt);
		printf("update: cnt=%d\n", cnt);
		db_free(stmt);
	}

	sql = "UPDATE [产品] SET [单价] = [单价] + ? WHERE [产品名称] = ?";
	stmt = db_prepare(sql);
	if (stmt != NULL)
	{
		dval = 0.25;
		db_set_double(stmt, 1, &dval);
		db_set_str(stmt, 2, "盐");
		db_exec_stmt(stmt, 0);
		cnt = db_affected_rows(stmt);
		printf("update: cnt=%d\n", cnt);
		db_free(stmt);
	}
}

int main()
{
	int ret;

	ret = db_connect("test.accdb");
	if (ret == -1)
	{
		printf("%s\n", db_geterror());
		return -1;
	}

	show_products();
	find_product_by_id(14);
	find_product_by_name("饼干");
	update_products();

	db_disconnect();
	return 0;
}

db.h:

#pragma once

typedef void *Statement;

/* 数据库连接与断开 */
int db_connect(const char *filename);
void db_disconnect();
const char *db_geterror();

/* Prepared Statement 相关 */
Statement db_prepare(const char *sql);
int db_exec_stmt(Statement stmt, int free);
// 设置SQL语句中的问号
void db_set_int(Statement stmt, int i, const int *p);
void db_set_str(Statement stmt, int i, const char *s);
void db_set_float(Statement stmt, int i, const float *p);
void db_set_double(Statement stmt, int i, const double *p);

/* 直接执行查询,不Prepare */
Statement db_query(const char *sql); // 执行SELECT查询,需要手动释放资源
int db_affected_rows(Statement stmt); // 获取INSERT、UPDATE和DELETE语句影响的行数
int db_exec(const char *sql, int *affected_cnt); // 执行普通查询
int db_has_records(const char *sql); // 直接执行SELECT查询,判断结果集是否有记录
void db_free(Statement stmt); // 释放db_query的资源

/* 记录集操作 */
int db_fetch(Statement stmt); // 获取一行记录
// 绑定字段到变量上
void db_bind_int(Statement stmt, int col, int *p); // 以整数类型保存第col列的内容
void db_bind_str(Statement stmt, int col, char *buf, int len, size_t *real_len); // 以字符串类型保存第col列的内容
void db_bind_float(Statement stmt, int col, float *p); // 以小数类型保存第col列的内容
void db_bind_double(Statement stmt, int col, double *p);
// 直接读取字段内容
int db_get_int(Statement stmt, int col); // 直接以整数类型读取第col列的内容
char *db_get_str(Statement stmt, int col, char *buf, int len, size_t *real_len); // 直接以字符串类型读取第col列的内容
float db_get_float(Statement stmt, int col); // 直接以小数类型读取第col列的内容
double db_get_double(Statement stmt, int col);

db.c:

/* 
这个文件里封装了很多操作数据库的函数
参考资料:https://msdn.microsoft.com/en-us/library/ms714562%28v=vs.85%29.aspx
*/
#include <stdio.h>
#include <Windows.h>
#include <sqlext.h>
#include "db.h"

#define DB_DSN "Driver={Microsoft Access Driver (*.mdb, *.accdb)};Dbq=%s;"

static char db_errmsg[500];
static BOOL db_connected;
static SQLHENV db_env;
static SQLHDBC db_dbc;

int db_connect(const char *filename)
{
	char fullpath[MAX_PATH] = ""; // 数据库文件完整路径
	char dsn[MAX_PATH + 100]; // 数据库连接字符串
	char code[6]; // 错误代码
	char msg[500]; // 错误信息
	char *p;
	SQLRETURN rc;

	rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &db_env);
	if (!SQL_SUCCEEDED(rc))
		goto err;
	rc = SQLSetEnvAttr(db_env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
	if (!SQL_SUCCEEDED(rc))
		goto err;
	rc = SQLAllocHandle(SQL_HANDLE_DBC, db_env, &db_dbc);
	if (!SQL_SUCCEEDED(rc))
		goto err;

	// 根据数据库文件名生成数据库连接字符串
	if (filename[1] != ':')
	{
		GetModuleFileNameA(NULL, fullpath, sizeof(fullpath));
		p = strrchr(fullpath, '\\');
		if (p != NULL)
			*(p + 1) = '\0';
	}
	strncat_s(fullpath, sizeof(fullpath), filename, sizeof(fullpath) - 1);
	_snprintf_s(dsn, sizeof(dsn), sizeof(dsn) - 1, DB_DSN, fullpath);
	
	// 根据连接字符串连接数据库
	rc = SQLDriverConnectA(db_dbc, NULL, dsn, SQL_NTS, NULL, 0, NULL, 0);
	if (!SQL_SUCCEEDED(rc))
	{
		// 获取连接失败的错误提示
		rc = SQLGetDiagRecA(SQL_HANDLE_DBC, db_dbc, 1, code, NULL, msg, sizeof(msg), NULL);
		goto err;
	}
	db_connected = TRUE;
	return 0; // 连接成功时,函数返回0
	
err:
	db_disconnect();
	if (SQL_SUCCEEDED(rc))
		_snprintf_s(db_errmsg, sizeof(db_errmsg), sizeof(db_errmsg) - 1, "连接数据库失败。\n错误代码: %s\n错误信息: %s", code, msg);
	else
		strcpy_s(db_errmsg, sizeof(db_errmsg), "未知错误。");
	return -1; // 连接失败时,函数返回-1
}

void db_disconnect()
{
	if (db_dbc != SQL_NULL_HDBC)
	{
		if (db_connected)
		{
			SQLDisconnect(db_dbc);
			db_connected = FALSE;
		}
		SQLFreeHandle(SQL_HANDLE_DBC, db_dbc);
		db_dbc = SQL_NULL_HDBC;
	}
	if (db_env != SQL_NULL_HENV)
	{
		SQLFreeHandle(SQL_HANDLE_ENV, db_env);
		db_env = SQL_NULL_HENV;
	}
}

const char *db_geterror()
{
	return db_errmsg;
}

Statement db_prepare(const char *sql)
{
	SQLHSTMT stmt;
	SQLRETURN rc;
 
	rc = SQLAllocHandle(SQL_HANDLE_STMT, db_dbc, &stmt);
	if (!SQL_SUCCEEDED(rc))
		return NULL;

	SQLPrepareA(stmt, (char *)sql, (int)strlen(sql));
	return stmt;
}

int db_exec_stmt(Statement stmt, int free)
{
	SQLRETURN rc;
	
	rc = SQLExecute(stmt);
	if (free)
		db_free(stmt);
 
	if (!SQL_SUCCEEDED(rc))
		return -1;
	return 0;
}

void db_set_int(Statement stmt, int i, const int *p)
{
	static SQLLEN size = sizeof(int); // 该变量的地址必须固定, 所以要加static
 
	SQLBindParameter(stmt, i, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, (int *)p, 0, &size); // 整型变量的地址也必须固定
}
 
void db_set_str(Statement stmt, int i, const char *s)
{
	static SQLLEN len = SQL_NTS;
 
	SQLBindParameter(stmt, i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, strlen(s), 0, (char *)s, 0, &len);
}

void db_set_float(Statement stmt, int i, const float *p)
{
	static SQLLEN size = sizeof(float);
 
	SQLBindParameter(stmt, i, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, 0, 0, (float *)p, 0, &size);
}

void db_set_double(Statement stmt, int i, const double *p)
{
	static SQLLEN size = sizeof(double);
 
	SQLBindParameter(stmt, i, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, 0, 0, (double *)p, 0, &size);
}

Statement db_query(const char *sql)
{
	SQLRETURN rc;
	Statement stmt;
	
	stmt = db_prepare(sql);
	if (stmt == NULL)
		return NULL;

	rc = SQLExecute(stmt);
	if (!SQL_SUCCEEDED(rc))
	{
		db_free(stmt);
		return NULL;
	}
	return stmt;
}

// 此函数只能用于获取INSERT、UPDATE和DELETE语句影响的行数
// 无法获取SELECT语句查询返回的记录集的行数
int db_affected_rows(Statement stmt)
{
	SQLLEN cnt;
	SQLRETURN rc;

	if (stmt == NULL)
		return -1;
 
	rc = SQLRowCount(stmt, &cnt);
	if (!SQL_SUCCEEDED(rc))
		return -1;
	return (int)cnt;
}

int db_exec(const char *sql, int *affected_cnt)
{
	Statement stmt;
	
	stmt = db_query(sql);
	if (affected_cnt != NULL)
		*affected_cnt = db_affected_rows(stmt);
	if (stmt == NULL)
		return -1;

	db_free(stmt);
	return 0;
}

int db_has_records(const char *sql)
{
	int ret;
	Statement stmt;
	
	stmt = db_query(sql);
	if (stmt == NULL)
		return 0;
 
	ret = db_fetch(stmt);
	db_free(stmt);
	return ret;
}

void db_free(Statement stmt)
{
	SQLFreeHandle(SQL_HANDLE_STMT, stmt);
}

int db_fetch(Statement stmt)
{
	SQLRETURN rc;
 
	rc = SQLFetch(stmt);
	if (rc == SQL_SUCCESS)
		return 1;
	else if (rc == SQL_NO_DATA)
		return 0;
	else
		return -1;
}

void db_bind_int(Statement stmt, int col, int *p)
{
	SQLBindCol(stmt, col, SQL_C_LONG, p, sizeof(int), NULL);
}
 
void db_bind_str(Statement stmt, int col, char *buf, int len, size_t *real_len)
{
	SQLBindCol(stmt, col, SQL_C_CHAR, buf, len, real_len);
}
 
void db_bind_float(Statement stmt, int col, float *p)
{
	SQLBindCol(stmt, col, SQL_C_FLOAT, p, sizeof(float), NULL);
}
 
void db_bind_double(Statement stmt, int col, double *p)
{
	SQLBindCol(stmt, col, SQL_C_DOUBLE, p, sizeof(double), NULL);
}
 
int db_get_int(Statement stmt, int col)
{
	int n;
	SQLRETURN ret;
 
	ret = SQLGetData(stmt, col, SQL_C_LONG, &n, sizeof(int), NULL);
	if (SUCCEEDED(ret))
		return n;
	else
		return 0;
}

char *db_get_str(Statement stmt, int col, char *buf, int len, size_t *real_len)
{
	SQLRETURN ret;

	ret = SQLGetData(stmt, col, SQL_C_CHAR, buf, len, real_len);
	if (!SUCCEEDED(ret))
	{
		buf[0] = '\0';
		if (real_len != NULL)
			*real_len = 0;
	}
	return buf;
}

float db_get_float(Statement stmt, int col)
{
	float n;
	SQLRETURN ret;
 
	ret = SQLGetData(stmt, col, SQL_C_FLOAT, &n, sizeof(float), NULL);
	if (SUCCEEDED(ret))
		return n;
	else
		return 0;
}

double db_get_double(Statement stmt, int col)
{
	double n;
	SQLRETURN ret;
 
	ret = SQLGetData(stmt, col, SQL_C_DOUBLE, &n, sizeof(double), NULL);
	if (SUCCEEDED(ret))
		return n;
	else
		return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1375052.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Airtest-Selenium实操小课:爬取新榜数据

1. 前言 最近看到群里很多小伙伴都在用Airtest-Selenium做一些web自动化的尝试&#xff0c;正好趁此机会&#xff0c;我们也出几个关于web自动化的实操小课&#xff0c;仅供大家参考~ 今天跟大家分享的是一个非常简单的爬取网页信息的小练习&#xff0c;在百度找到新榜网页&am…

【C初阶——指针4】鹏哥C语言系列文章,基本语法知识全面讲解——指针(4)

本文由睡觉待开机原创&#xff0c;转载请注明出处。 本内容在csdn网站首发 欢迎各位点赞—评论—收藏 如果存在不足之处请评论留言&#xff0c;共同进步&#xff01; 这里写目录标题 前言1.回调函数2.qsort函数的使用3.qsort函数的模拟实现 思路大纲&#xff1a; 前言 本节博…

嵌入式培训机构四个月实训课程笔记(完整版)-Linux系统编程第十天-Linux下mplayer音乐播放器练习题(物联技术666)

更多配套资料CSDN地址:点赞+关注,功德无量。更多配套资料,欢迎私信。 物联技术666_嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记-CSDN博客物联技术666擅长嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记,等方面的知识,物联技术666关注机器学习,arm开发,物联网,嵌入式硬件,单片机…

【每日一题】2696. 删除子串后的字符串最小长度-2024.1.10

题目&#xff1a; 2696. 删除子串后的字符串最小长度 给你一个仅由 大写 英文字符组成的字符串 s 。 你可以对此字符串执行一些操作&#xff0c;在每一步操作中&#xff0c;你可以从 s 中删除 任一个 "AB" 或 "CD" 子字符串。 通过执行操作&#xff0c…

Element|Upload结合Progress实现上传展示进度条

背景 &#xff1a; 项目里的 附件上传 题型组件&#xff0c;用户在上传过程中&#xff0c;如果文件较大&#xff0c;上传过程较慢&#xff0c;而又没有一个类似 Loading... 的加载过程的话&#xff0c;会显得干愣愣的&#xff0c;用户体验较差&#xff0c;所以需要添加一个进度…

Springboot+vue的毕业论文管理系统(有报告)。Javaee项目,springboot vue前后端分离项目

演示视频&#xff1a; Springbootvue的毕业论文管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的毕业论文管理系统&#xff0c;采用M&#xff08;model&…

JWT---JSON Web Token

JSON Web Token是什么 JSON Web Token (JWT)是一个开放标准(RFC 7519)&#xff0c;它定义了一种紧凑的、自包含的方式&#xff0c;用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任&#xff0c;因为它是数字签名的。 JSON Web Token的结构是什么样的 JSON…

Handsfree_ros_imu:ROS机器人IMU模块ARHS姿态传感器(A9)Liunx系统Ubuntu20.04学习启动和运行教程

这个是篇学习 Handsfree_ros_imu 传感器的博客记录 官方教程链接见&#xff1a; https://docs.taobotics.com/docs/hfi-imu/ 产品功能 IMU 内有 加速度计&#xff0c;陀螺仪&#xff0c;磁力计这些传感器&#xff0c;通过固定 imu 到物体上后&#xff0c;可以获取物体在运动…

登录模块的实现

一.前期的准备工作 1.页面的布局 (1)表单的校验: 利用element-ui提供的文档绑定rules规则后实现校验 (2)跨域的配置 &#xff1a; 利用proxy代理来解决跨域的问题 (3)axios拦截器的配置 两个点:1. 在请求拦截的成功回调中,如果token,因为调用其它的接口需要token才能调取。 在请…

锤科HandShaker修改版,支持安卓14、澎湃OS

如今几乎各家手机厂商都在布局生态&#xff0c;但PC端往往是最容易被忽略的一环&#xff0c;哪怕是很强的华为鸿蒙、小米澎湃&#xff0c;想要做到手机和电脑互联&#xff0c;也限制了笔记本机型 虽然我一直致力于解锁非小米电脑安装小米电脑管家&#xff0c;比如前几天刚刚更…

在WindowsServer2012中部署war项目

目录 前言 一.jdk安装 二.Tomact安装 三.MySQL安装 ​编辑​编辑​编辑​编辑​编辑​编辑​编辑 四.开放端口号 MySQL开放端口号 Tomact开放端口号 ​编辑 五.项目部署 1.将war放置在tomact中 2.配置项目sql脚本 3.最终效果 前言 安装Java开发工具包&#xff08…

【设计模式-02】Strategy策略模式及应用场景

一、参考资料 Java 官方文档 Overview (Java SE 18 & JDK 18)module indexhttps://docs.oracle.com/en/java/javase/18/docs/api/index.html Java中使用到的策略模式 Comparator、comparable Comparator (Java SE 18 & JDK 18)declaration: module: java.base, pa…

构建免费的Dokan和WooCommerce构建线上课程市场在线销售数字课程

我们知道创建良好的学习说明和材料很困难。但当涉及到销售时&#xff0c;就变得更加困难。如果您无法出售您的课程&#xff0c;那么没有什么比这更令人沮丧的了。 幸运的是&#xff0c;如果您使用的是 WordPress 网站&#xff0c;那么您可以非常轻松且免费地完成此操作。借助L…

java SSM物业管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM物业管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和 数据库&#xff0c;系统主要采用B/…

2024.1.11 Kafka 消息队列,shell命令,核心原理

目录 一 . 消息队列 二. Kafka 三 . 启动命令 四 . Kafka的Shell 命令 五 . Kafka的核心原理 1. Topic的分区和副本机制 2 . 消息存储机制 和 查询机制 3. Kafka中生产者数据分发策略 六 . Kafka 之所以具有高速的读写性能&#xff0c;主要有以下几个原因 七. 笔记…

指导AI进行推理:提示工程如何弥补RAG系统中的差距

每日推荐一篇专注于解决实际问题的外文,精准翻译并深入解读其要点,助力读者培养实际问题解决和代码动手的能力。 欢迎关注公众号(NLP Research) 原文标题:Instructing AI to Reason: How Prompt Engineering Bridges the Gap in RAG Systems 原文地址:https://medium.c…

vscode配置Todo Tree插件

一、在VSCode中安装插件Todo Tree ​​​​ 二、按下快捷键ctrlshiftP&#xff0c;输入setting.jspn 选择相应的配置范围&#xff0c;我们选择的是用户配置 Open User Settings(JSON)&#xff0c;将以下代码插入其中。 //todo-tree 标签配置从这里开始 标签兼容大小写字母(很…

MoE模型性能还能更上一层楼?一次QLoRA微调实践

Fine-Tuning Mixtral 8x7B with QLoRA&#xff1a;Enhancing Model Performance &#x1f680; 编者按&#xff1a;最近&#xff0c;混合专家(Mixture of Experts,MoE)这种模型设计策略展现出了卓越的语言理解能力&#xff0c;如何在此基础上进一步提升 MoE 模型的性能成为业界…

React 18中新钩子 useDeferredValue 使用

React是一个流行的用于构建用户界面的JavaScript库,它不断发展以为开发人员提供优化性能的工具。 React 18中引入的此类工具之一是useDeferredValue钩子,它旨在通过优先渲染更新来提高应用程序的性能。 useDeferredValue钩子是什么? useDeferredValue钩子是React性能优化工…

c++析构函数

析构函数的简述 1. 析构函数和构造函数类似&#xff0c;是c规定当对象的生命周期结束时&#xff0c;默认你会调用析构函数。 2. 同理&#xff0c;当我们不写析构函数的时候&#xff0c;编译器会自动生成一个空实现的析构函数。 3. 析构函数只能编译器自己调用&#xff0c;我们…