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;并可选择显示…

如何设置 QTableView 表格每列数据的对齐方式?

要设置 QTableView 表格每列数据的对齐方式&#xff0c;您可以使用 QStandardItemModel 的 setData() 方法来为每个单元格设置对齐方式。以下是一个示例&#xff1a; // 创建数据模型和设置表头略...// 设置列对齐方式 model->setData(model->index(0, 0), Qt::AlignLef…

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

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

EasyExcel的追加写入(新增POI、CSV)

总结&#xff1a;目前市面上流行的2种 EasyExcel和POI都不是真正的对物理excel文件进行追加导入。只是在缓存里面追加&#xff0c;最后一次性写入&#xff0c;并不能解决内存占用问题。 1.EasyExcel2.POI3.CSV 无非就是下面两种逻辑&#xff1a; 1.for循环查询数据&#xff0c;…

js中对数字,超大金额(千位符,小数点)格式化处理

前言 这个问题的灵感来自线上一个小bug&#xff0c;前两天刚看完同事写的代码&#xff0c;对数字类型处理的很好&#xff0c;之前一直都是用正则和toFixed(2)处理数字相关&#xff0c;后面发现使用numeral.js处理更完美。 对于下面这种数据的处理&#xff0c;你能想到几种方法…

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

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

Git 大量log查看:git log --pretty=oneline

git log 是 Git 版本控制系统中的一个命令&#xff0c;用于展示一个或多个分支的提交历史记录。 当你在 git log 命令后面添加 --prettyoneline 选项时&#xff0c;它会以单行的形式显示提交历史。这意味着每个提交将会在一行内显示&#xff0c;而不是默认的多行显示格式。 具…

浅谈WPF之Popup弹出层

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

Python Matplotlib 库使用基本指南

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

FindTheIndexOfTheFirstOccurrenceInAString 【找到第一个匹配的下标】

双指针 字串的所有的字符都匹配完&#xff0c;匹配成功。 如果要与之匹配的字符串剩下的长度小于字串的长度&#xff0c;即剩下的已经不会再满足。 public int strStr(String haystack, String needle) {int index -1, i0, j0;for(i0; i<haystack.length()-needle.length(…

贪心算法Day06

#738.单调递增的数字 力扣题目链接(opens new window) 给定一个非负整数 N&#xff0c;找出小于或等于 N 的最大的整数&#xff0c;同时这个整数需要满足其各个位数上的数字是单调递增。 &#xff08;当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们…

【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.解…

Qt之数据转换与处理

从串口读取到的QByteAray数据一般需要进行提取和解析&#xff0c;此时就需要将QByteArray数拒转换为各种类型的数据。常用的转换包括&#xff1a; &#xff08;1&#xff09; 转为Hex&#xff0c;用于显示十六进制&#xff0c;这点在调试时特别有用&#xff0c;因为大多HEX码是…

【Vue技巧】Vue2和Vue3组件上使用v-model的实现原理

ChatGPT4.0国内站点&#xff0c;支持GPT4 Vision 视觉模型&#xff1a;海鲸AI 在Vue中&#xff0c;v-model 是一个语法糖&#xff0c;用于在输入框、选择框等表单元素上创建双向数据绑定。当你在自定义组件中实现 v-model 功能时&#xff0c;你需要理解它背后的原理&#xff1a…

DFA算法实战-敏感词过滤

前言 这里的项目实战, 我们使用的是 SpringBoot2.xJDK1.8搭建的,核心思想是借助了Hutool工具类的 WordTree。想了解更多DFA算法的实现可以参考DFA算法的实现 实战案例 1. 引入Hutool的工具类 <dependency><groupId>cn.hutool</groupId><artifactId>…

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

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

mysql group by 之后要把名字放到一个字段里边

分组后的其他字段去重 count(DISTINCT(product)) num_sold,分组后数据存放到一个字段 &#xff08;带着排序&#xff0c;在保存到一个字段&#xff09; group_concat(distinct product order by product separator ‘,’) select sell_date,count(DISTINCT(product)) num_so…