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

编译环境: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 oncetypedef 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; // 连接成功时,函数返回0err:db_disconnect();if (SQL_SUCCEEDED(rc))_snprintf_s(db_errmsg, sizeof(db_errmsg), sizeof(db_errmsg) - 1, "连接数据库失败。\n错误代码: %s\n错误信息: %s", code, msg);elsestrcpy_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); // 该变量的地址必须固定, 所以要加staticSQLBindParameter(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;elsereturn -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;elsereturn 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;elsereturn 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;elsereturn 0;
}

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

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

相关文章

【Linux】Linux 系统编程——tree 命令

文章目录 1. 命令概述2. 命令格式3. 常用选项4. 相关描述4.1 tree 命令安装 5. 参考示例5.1 创建树形目录5.2 使用 tree 命令查看树形目录 1. 命令概述 tree 命令用于在命令行界面以树状图形式显示目录及其子目录的内容。这个命令递归地列出所有子目录&#xff0c;并可选择显示…

C#,迭代深化搜索(IDS)或迭代深化深度优先搜索(IDDFS)算法的源代码

摘要&#xff1a;本文介绍适合于大数据规模情况下的&#xff0c;新型的迭代深化深度优先搜索(IDDFS)算法的原理、实例及实现的C#源代码。 引言 常用的树&#xff08;或图&#xff09;遍历算法是两种&#xff1a; 广度优先搜索算法&#xff08;BFS&#xff09; 和 深度优先搜索…

燃情瞬间,智能酒精壁炉点亮户外聚会新潮流

在户外聚会中&#xff0c;一种备受瞩目的装饰品和功能性家居设备正逐渐崭露头角&#xff0c;那就是智能酒精壁炉。这种独特的户外装置不仅为聚会场合带来独特的氛围&#xff0c;还具有许多引人注目的优势。 其明亮的火焰不仅照亮整个场所&#xff0c;还散发出温暖迷人的光芒&am…

浅谈WPF之Popup弹出层

在日常开发中&#xff0c;当点击某控件时&#xff0c;经常看到一些弹出框&#xff0c;停靠在某些页面元素的附近&#xff0c;但这些又不是真正的窗口&#xff0c;而是页面的一部分&#xff0c;那这种功能是如何实现的呢&#xff1f;今天就以一个简单的小例子&#xff0c;简述如…

Python Matplotlib 库使用基本指南

简介 Matplotlib 是一个广泛使用的 Python 数据可视化库&#xff0c;它可以创建各种类型的图表、图形和可视化效果。无论是简单的折线图还是复杂的热力图&#xff0c;Matplotlib 提供了丰富的功能来满足我们的数据可视化需求。本指南将详细介绍如何安装、基本绘图函数以及常见…

【Redis】Redis面试热点

Redis 集群有哪些方案&#xff1f; 主从复制&#xff1a;解决了高并发问题 哨兵模式&#xff1a;解决了高并发&#xff0c;高可用问题 分片集群&#xff1a;解决了海量数据存储&#xff0c;高并发写的问题 主从复制 图示&#xff1a; 主从复制&#xff1a;单节点 Redis 并发…

NPN PNP SS8050 SS8550 S8050

SS8050的使用及引脚判断方法 今天讲的是NPN型三极管SS8050&#xff0c;主要分为以下几个方面&#xff1a; 一、前言 二、SS8050简介 三、NPN三极管与PNP三极管 四、三极管管脚识别方法 五、不拆卸三极管判断其好坏 六、S8050和SS8050的区别 七、三极管与MOS管的区别 八…

nginx基础面试题以及配置文件解析和命令控制

目录 1、nginx是什么 2、nginx的特点 3、为什么中国大陆有&#xff1a;百度、京东、新浪、网易、腾讯、淘宝等这么多用户使用nginx 4、nginx 的内部技术架构 上一期我们配置安装了nginx接着讲一下nginx配置文件的解析和nginx 命令控制 感谢观看&#xff01;希望能够帮助到…

mapper向mapper.xml传参中文时的乱码问题

1.起因&#xff1a; 在idea中进行模糊查询传参时&#xff0c;发现在idea中查中文查不出记录&#xff0c;在navicate中可以查出来。 2.猜测&#xff1a; 1.idea中的编码问题导致的乱码。 2.idea和navicate的编码一致性导致的乱码。 3.mapper向mapper.xml传参后出现乱码。 3.解…

Python 基础【八】--数据类型-字典【2024.1.11】

1.定义 字典的内容在花括号 {} 内&#xff0c;键-值&#xff08;key-value&#xff09;之间用冒号 : 分隔&#xff0c;键值对之间用逗号 , 分隔&#xff0c;比如创建字典 &#xff0c;如下所示&#xff1a; d{name:小明,age:18}# 使用 dict 函数&#xff1a;强转 # 方式一&am…

【一文详解】知识分享:(ASP.Net Core基础学习及快速入门)

背景知识 相关术语 .Net .NET是微软的一个开发平台&#xff0c;这个平台的一大特点就是跨语言性&#xff0c;不管是什么语言&#xff0c;c、c、c#、F#、J#、vb等语言都可以用这个平台合作开发&#xff1b; .NET&#xff0c;它是微软创建的一个用于构建多种不同类型的应用程…

书生·浦语大模型实战营-学习笔记2

目录 轻松玩转书生浦语大模型趣味Demo1. 大模型及 InternLM 模型介绍2. InternLM-Chat-7B 智能対话 Demo3. Lagent 智能体工具调用 Demo4. 浦语•灵笔图文创作理解 Demo5. 通用环境配置实验记录6. 课后作业 视频地址&#xff1a; (2)轻松玩转书生浦语大模型趣味Demo 文档教程&a…

C#用string.Replace方法批量替换某一类字符串

目录 一、关于字符串及其操作常识 二、String.Replace 方法 1.重载 2.Replace(Char, Char) 3.Replace(String, String) &#xff08;1&#xff09;实例&#xff1a; &#xff08;2&#xff09;生成结果&#xff1a; 4.Replace(String, String, StringComparison) 5.…

2023年低代码无代码产业大会:核心内容与学习收获(附大会核心PPT下载)

2023年低代码无代码产业大会聚焦了行业最前沿的趋势与技术&#xff0c;为企业数字化转型提供新的思路和方法。在这次大会上&#xff0c;我们可以深入了解到低代码/无代码技术的最新发展&#xff0c;以及如何利用这些技术来提高业务效率和创新能力。 一、大会的核心内容 1、低代…

net8 golang python性能比较

net8正式版出来两个月&#xff0c;现在性能到底如何呢&#xff0c;做个简单的例子和其他语言比较一下&#xff0c;测试内容是查找1000000以内的质数&#xff0c;代码不多&#xff0c;但包含了循环计算和Math库函数调用&#xff0c;直观的看一下语言之间差距是多少&#xff0c;心…

【AI视野·今日Robot 机器人论文速览 第七十三期】Tue, 9 Jan 2024

AI视野今日CS.Robotics 机器人学论文速览 Tue, 9 Jan 2024 Totally 40 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers Digital Twin for Autonomous Surface Vessels for Safe Maritime Navigation Authors Daniel Menges, Andreas Von Brandis, A…

JWT---JSON Web Token

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

【每日一题】构造有效字符串的最少插入数

文章目录 Tag题目来源解题思路方法一&#xff1a;考虑 abc 的个数 写在最后 Tag 【字符串】【2024-01-11】 题目来源 2645. 构造有效字符串的最少插入数 解题思路 方法一&#xff1a;考虑 abc 的个数 思路 如果答案由 t 个 “abc” 组成&#xff0c;那么需要插入字符个数为…

概率论与数理统计-第7章 假设检验

假设检验的基本概念 二、假设检验的基本思想 假设检验的基本思想实质上是带有某种概率性质的反证法&#xff0c;为了检验一个假设H0,是否正确&#xff0c;首先假定该假设H0正确&#xff0c;然后根据抽取到的样本对假设H0作出接受或拒绝的决策&#xff0c;如果样本观察值导致了…

二叉树的创建与遍历

对于前序遍历&#xff0c;首先访问当前节点&#xff0c;然后递归地遍历左子树和右子树。 这就是为什么前序遍历的代码中&#xff0c;首先是 printf("%d ", root->data);。中序遍历&#xff1a; 对于中序遍历&#xff0c;首先递归地遍历左子树&#xff0c;然后访问…