C语言连接Mysql数据库
本小白写的第一个博客,请多关照!
一路上太多坑了,我把我遇到了的觉得有价值的坑都记录下来,既是自己的记录成长,也方便了以后的广大小伙伴。 [全文较长 建议打开文章目录食用]
这次目标具体如图,
一下还不太好下手有点懵哈,不管怎样,先学起来,在最大的视频学习网站上找到了不错的mysql数据库教程,戳这里看看。
我的操作过程大概分下面几步
- MySQL学习与软件安装
- MySQl配置问题
- C连接MySQL配置
- 编写函数体、代码
- 修复一些bug
MySQL语法学习软件安装
一开始没看b站上的教程视频,自己去官网下了zip版,有装php时奔溃内味了。 装好了能登录但是一写命令就出错,没办法只好卸了。后来下的msi版,安装难度就不大了。
小白视角: 不同的账号都能进数据库,不过权限可能不一样,能进行的操作不一样。数据库里可以有很多表,表里可以放信息,你能通过sql语句对数据库、表操作,实现增删改查。有了图形化工具比如navicat更方便,就像你操作电脑文件夹,操作excel一样简单。
MySQL配置问题
翻各大博客各个帖子,按着教程,解决了卸载不干净,服务启动失败等等的问题,服务MySQL能用了,但是出现了表里不能插入中文数据,会报错,的现象set names gbk也没用。修改my.ini配置文件更改C/S(客户端/服务端)两端的字符集就行了.说着轻松,但是我修改my.ini之后 mysql服务会启动失败,启动会卡住,这问题卡了我好久。后来才知道 my.ini默认的编码是ANSI。我更改my.ini信息后自动以UTF8编码保存了,更改配置后只要用记事本打开,另存为以ANSI编码保存,重启电脑重启服务就行了。
我的详细解决方法 另一篇文章:【修改my.ini保存后MySQL服务正在启动或停止中,启动失败】
C连接 MySQL
一开始真的毫无头绪,对API接口还没什么概念。过了好久我才知道,要看官方文档 C API,里面给了连接方法说明之类的,又看了博客的攻略,包含.h头文件,链接lib文件跟dll文件,终于搞定这块最难啃的骨头,以此了解了.h文件 dll文件 lib文件的一些关系,对API也有了新的认识
在vs里新建一个c++控制台项目,把源文件名字改成.c就能用c语言写了.
接下来就要引入.h头文件和dll,lib库。
引入mysql.h头文件
:注意自己的系统是几位的,我是64位的所以选择了x64来运行,不然看不到右边解决方案资源管理器的外部依赖项
把你mysql根目录下的include、lib目录分别贴进去。
如果成功,你应该能读取到mysql.h。
如果还是读取不了mysql.h,你还可以试试在这里加入include目录。
连接头文件是不够的,头文件只给了函数原型,是执行不了函数的,接下来还要链接dll库和lib库。
链接lib库和dll库
链接器->输入->附加依赖项里增加一项libmysql.lib;然后在mysql根目录下进到lib文件夹,把里面的libmysql.dll和libmysql.lib拷贝粘贴到项目.c文件同目录下。
开始连接MySQL
首先要看一下官方的C API文档 https://dev.mysql.com/doc/refman/5.7/en/c-api-data-structures.html
MYSQL结构是数据库连接的处理程序,很重要很基本就是了,要连接,先整他一两个。
mysql_init()初始化连接程序函数:
MYSQL *mysql_init(MYSQL *mysql) 是初始化连接函数,他传入的参数是连接程序的地址,返回初始化后的地址,因此不要忘记加’&’。初始化连接后记得最后要关闭。
mysql_real_connect()连接函数:
MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag)
第一个参数是一个数据库连接处理程序的地址,第二个是mysql的主机或ip,如果是NULL或者"localhost"则连接本机。后面的user是数据库用户,passwd是密码,port是端口,大家应该都用3306吧。unix_socket我也不太懂,但是给他NULL无伤大雅,client_flag通常是0,他可以是别的值,启用更多的功能。
mysql_error()报错函数:
const char *mysql_error(MYSQL *mysql) 他会返回这个连接最近的错误信息字符串。
mysql_close()关闭连接程序函数:
关闭之前的连接。
我用一个sock MYSQL连接指针 来记录我连接上了mysql数据库,后续对数据库的操作都要用到 mysql连接 的地址 作为参数。
/*连接数据库*/
#include <stdio.h>
#include <mysql.h>
MYSQL mysql, * sock;
int main()
{
mysql_init(&mysql); /*把这些参数换成你自己的数据 就连接成功了*/
if ((sock = mysql_real_connect(&mysql, host, user, passwd, db, port, unix_socket, client_flag)) != NULL) //连接MySQL
{
printf("连接MySQL成功!!\n");
}
else
{
printf("连接失败,原因是: \n");
fprintf(stderr, " %s\n", mysql_error(&mysql));
exit(1);
}
mysql_close(&mysql);
mysql_close(sock);
return 0;
}
恭喜连接成功!
连接心得
h头文件里声明函数原型 ,动态库:dll 静态库lib 来实现函数功能
(1)编写程序时,你要包含(#include “什么.h”) dll文件作者提供 的 头文件(.h文件) 。程序里,便和普通函数调用一样,去调用它的函数。
(2)程序编译时,你要链接 dll文件作者提供 的 (.lib文件) 库文件。当然,你可以在源程序里把.lib 名字 写上,编译时自动去链接,例子: #pragma comment (lib, “什么.lib”)
(3)执行时,要有 .dll 文件. 放在当前文件夹或系统文件夹里。
- 当使用静态库lib时
这个lib是包含了函数的执行代码的,只需要include h文件并在编译选项里正确链接lib即可。 - 当使用动态库dll时,有两种情况
- 只有dll而没有.h和.lib而有api文档的情况下,可以通过LoadLibrary动态加载dll,并通过GetProcAddress来获取函数地址并使用。
- 有dll,有.h,有lib的话,在编译选项里正确链接lib,并在代码头文件中includ .h文件即可使用
其中dll存储了函数具体的执行代码和资源,.h文件给出了函数的原型,.lib文件给出了函数在dll中的内存偏移地址。使用时dll与exe文件在同一目录即可
有点绕 到时不会再找攻略好了。[手动狗头]
编写函数体、代码
这个阶段主要就是看C API文档 看函数说明 使用方法,看网上的代码来参考参考。
一边看文档一边学英语! 哪里不会点哪里(浏览器翻译插件)。虽然还是整页翻译用的多 这里立个flag,今年四级冲冲冲我要免修必修大英!!
能连上mysql之后心情就好起来了 舒服!接下来就是按着文档、网上资料写增删改查的函数。然鹅搞懂函数写函数也花了我不少时间
SQL语句函数
所有的增删改查命令都是通过sql语句来实现的,只要在C这里发送一个SQL命令字符串,MySQL服务器就能接受,执行,并返回信息。下面看看主要的实现函数和过程。
C API预处理语句
我了解了一下,为了最简单完成这个项目,我没用到预处理。
MySQL客户端/服务器协议提供了预处理语句。该功能采用了由mysql_stmt_init()初始化函数返回的MYSQL_STMT语句处理程序数据结构。对于多次执行的语句,预处理执行是一种有效的方式。首先对语句进行解析,为执行作好准备。接下来,在以后使用初始化函数返回的语句句柄执行一次或多次。
对于多次执行的语句,预处理执行比直接执行快,主要原因在于,仅对查询执行一次解析操作。在直接执行的情况下,每次执行语句时,均将进行查询。此外,由于每次执行预处理语句时仅需发送参数的数据,从而减少了网络通信量。
预处理语句的另一个优点是,它采用了二进制协议,从而使得客户端和服务器之间的数据传输更有效率。
mysql_query()函数
int mysql_query(MYSQL *mysql, const char *stmt_str) 第一个参数是连接的地址,第二个参数是查询语句字符串,注意不用自己加分号或\g结尾。(有无预处理都可)。
如果执行成功函数返回0,执行失败函数返回非0。
假设我当前的数据库test有stuinfo这张表
stuid | stuname | stuscore |
---|---|---|
1 | Amy | 80 |
2 | Jack | 96 |
3 | Mike | 59 |
如果我执行语句mysql_query(&mysql, “select * from stuinfo”); 执行成功,返回值为0,那我要的查询结果在哪里呢?从查询返回的信息叫结果集。 MYSQL_RES 结构 表示返回的结果集。我们可以定义一个全局变量表示结果集,例如 MYSQL_RES * result;
可以表示result是结果集的地址。
从查询中得到什么结果?
除了得到结果集外,还能得到这些信息
- my_ulonglong mysql_affected_rows(MYSQL *mysql) 函数返回了上一次查询中受到影响的列数。
- unsigned int mysql_num_fields(MYSQL_RES *result) 函数返回了结果集中的列数。
- mysql_num_rows()函数能返回结果集中的行数,和上面的函数差不多。
有结果集的处理
我们知道,有结果集返回的SQL语句有 SELECT,SHOW,DESCRIBE,EXPLAIN等 接下来我们要获取结果集并打印相关信息。还是上面的例子。
获取结果集
mysql_store_result():
MYSQL_RES *mysql_store_result(MYSQL *mysql) 函数返回的是带有结果集的 MYSQL_RES结构体指针。结果集用完了记得要调用 mysql_free_result();
调用mysql_query()后,你必须使用mysql_store_result()或者mysql_use_result() (这个我没用过)来获取每个成功产生的结果集。因此你可以这样写result = mysql_store_result(&mysql);
打印结果集
-
MYSQL_FIELD 结构是字段(列)信息的结构,MYSQL_ROW是结果集中一行信息的结构。
-
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result)用于检索下一字段(列)信息,没有下一字段时将返回NULL。
-
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)用于检索下一行信息,没有下一行时返回NULL。
#include <windows.h>
#include "stdio.h"
#include <conio.h>
#include <string.h>
#include "mysql.h"
MYSQL mysql, * sock; //声明MYSQL*连接处理程序 sock指socket
MYSQL_RES* result; //查询结果集
MYSQL_ROW row; //代表的是结果集中的一行
MYSQL_FIELD* field; //包含字段信息的结构指针
unsigned int num_fields;//表的列数
unsigned int num_rows;//表的行数
if (mysql_query(&mysql, "select * from stuinfo"))//执行成功函数返回0,执行失败函数返回非0。
{
printf("mysql_query failed.\n");
}
result = mysql_store_result(&mysql);
if (result)//有返回结果集
{
num_fields = mysql_num_fields(result);
printf("Affacted %lu rows.\n\n", (unsigned long)mysql_affected_rows(&mysql));
for (int i = 0; field = mysql_fetch_field(result); i++)
{
//获得属性名
printf("%24s",field->name); // field->name == (*field).name
printf(" |");
}
printf("\n");
while (row = mysql_fetch_row(result))
{
for (int i = 0; i < num_fields; i++)
{
printf("%25s", row[i]);
printf("|");
}
printf("\n");
}
}
打印结果集的方式多种多样,大家可以去探索试试。
无结果集的处理
有些命令不返回结果集,result = mysql_store_result(&mysql);
result为NULL,比如use , insert into ,update。结果集为空有两种情况,一是连接出错了,二是命令本来就不返回结果集。
unsigned int mysql_field_count(MYSQL *mysql) 返回最新查询的列数,如果它为0,那么结果集为NULL就是合理的,查询就是对的。
代码如下
if (mysql_query(&mysql, "UPDATE stuinfo SET stuscore = 68 WHERE stuid = 2"))
{
printf("mysql_query failed.\n");
}
result = mysql_store_result(&mysql);
if (result) //有返回结果集
{
num_fields = mysql_num_fields(result);
printf("Affacted %lu rows.\n\n", (unsigned long)mysql_affected_rows(&mysql));
while (field = mysql_fetch_field(result))
{
//获得属性名
printf("%24s",field->name);
printf(" |");
}
printf("\n");
while (row = mysql_fetch_row(result))
{
for (int i = 0; i < num_fields; i++)
{
printf("%25s", row[i]);
printf("|");
}
printf("\n");
}
}//有返回
else //结果集是NULL没有数据
{
if (mysql_errno(&mysql))//连接出错
{
fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
}
else if (mysql_field_count(&mysql) == 0)//
{
// query does not return data
// (it was not a SELECT)
printf("Affacted %lu rows.\n", (unsigned long)mysql_affected_rows(&mysql));
printf("执行成功\n\n");
}
}
mysql_free_result(result);
连接-查询总代码
#include <stdio.h>
#include <mysql.h>
MYSQL mysql,*sock;
MYSQL_RES * result;
MYSQL_ROW row;
MYSQL_FIELD * field;
unsigned int num_fields;
int main()
{
mysql_init(&mysql);
if ((sock = mysql_real_connect(&mysql, "", "root", "123456", "test", 3306, NULL, 0)) != NULL) //连接MySQL
{
printf("连接MySQL成功!!\n");
}
else
{
system("cls");
printf("连接失败,原因是: \n");
fprintf(stderr, " %s\n", mysql_error(&mysql));
exit(1);
}
if (mysql_query(&mysql, "select * from stuinfo"))
{
printf("mysql_query failed.\n");
}
result = mysql_store_result(&mysql);
if (result) //有返回结果集
{
num_fields = mysql_num_fields(result);
printf("Affacted %lu rows.\n\n", (unsigned long)mysql_affected_rows(&mysql));
while (field = mysql_fetch_field(result))
{
printf("%24s", field->name);
printf(" |");//获得属性名
}
printf("\n");
while (row = mysql_fetch_row(result))
{
for (int i = 0; i < num_fields; i++)
{
printf("%25s", row[i]);
printf("|");
}
printf("\n");
}
}
else //结果集是NULL没有数据
{
if (mysql_errno(&mysql))//连接出错
{
fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
}
else if (mysql_field_count(&mysql) == 0)//
{
// query does not return data
// (it was not a SELECT)
printf("Affacted %lu rows.\n", (unsigned long)mysql_affected_rows(&mysql));
printf("执行成功\n\n");
}
}
mysql_free_result(result);
mysql_close(&mysql);
mysql_close(sock);
return 0;
}
有了这个模型,就能封装成函数,做其他指令了!
封装函数
接下来封装一些简单常用的函数,比如SHOW DATABASES; DROP DATABASE XX;
有些命令有参数,定义两个数组,前一个是主功能SQL语句,后一个是参数,strcat连起来就行了。
void showDB()
{
if (mysql_query(&mysql, "show databases"))
{
printf("mysql_query failed.\n");
}
result = mysql_store_result(&mysql);
if (result)//有返回结果集
{
num_fields = mysql_num_fields(result);
printf("Affacted %lu rows.\n\n", (unsigned long)mysql_affected_rows(&mysql));
for (int i = 0; field = mysql_fetch_field(result); i++)
{
//获得属性名
printf("%24s", field->name);
printf(" |");
}
printf("\n-------------------------|\n");
while (row = mysql_fetch_row(result))
{
for (int i = 0; i < num_fields; i++)
{
printf("%25s", row[i]);
printf("|");
}
printf("\n");
}
}
mysql_free_result(result);
}
void showTables()
{
if (mysql_query(&mysql, "show tables"))
{
printf("mysql_query failed.\n");
}
result = mysql_store_result(&mysql);
if (result)//有返回结果集
{
num_fields = mysql_num_fields(result);
printf("Affacted %lu rows.\n\n", (unsigned long)mysql_affected_rows(&mysql));
for (int i = 0; field = mysql_fetch_field(result); i++)
{
//获得属性名
printf("%24s", field->name);
printf(" |");
}
printf("\n-------------------------|\n");
while (row = mysql_fetch_row(result))
{
for (int i = 0; i < num_fields; i++)
{
printf("%25s", row[i]);
printf("|");
}
printf("\n");
}
}
mysql_free_result(result);
}
void createDB()
{
char query[80] = "create database ";
char dbName[20];
printf("输入要创建的数据库名:");
gets(dbName);
strcat(query, dbName);
if (!mysql_query(&mysql, query))
{
printf("创建成功\n");
}
else
{
if (mysql_errno(&mysql))//出错
{
printf("创建失败。\n");
fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
}
}
}
void deleteDB()
{
char query[80] = "drop database ";
char dbName[20];
printf("输入要删除的数据库名: ");
gets(dbName);
strcat(query, dbName);
if (!mysql_query(&mysql, query))
printf("删除成功。\n");
else
{
if (mysql_errno(&mysql))//出错
{
printf("删除失败。\n");
fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
}
}
}
void selectDB()
{
char query[80] = "use ";
char dbName[20];
printf("输入要使用(use)的数据库名: ");
gets(dbName);
strcat(query, dbName);
if (!mysql_query(&mysql, query))
printf("正在使用%s。\n",dbName);
else
{
if (mysql_errno(&mysql))//出错
{
printf("操作失败。\n");
fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
}
}
}
void queryOperate()
{
char query[80];
printf("输入SQL语句:");
gets(query);
if (mysql_query(&mysql, query))
{
printf("mysql_query failed.\n");
}
result = mysql_store_result(&mysql);
if (result) //有返回结果集
{
num_fields = mysql_num_fields(result);
printf("Affacted %lu rows.\n\n", (unsigned long)mysql_affected_rows(&mysql));
while (field = mysql_fetch_field(result))
{
printf("%24s",field->name);
printf(" |");//获得属性名
}
printf("\n");
while (row = mysql_fetch_row(result))
{
for (int i = 0; i < num_fields; i++)
{
printf("%25s", row[i]);
printf("|");
}
printf("\n");
}
}
else //结果集是NULL没有数据
{
if (mysql_errno(&mysql))//连接出错
{
fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
}
else if (mysql_field_count(&mysql) == 0)//
{
// query does not return data
// (it was not a SELECT)
printf("Affacted %lu rows.\n", (unsigned long)mysql_affected_rows(&mysql));
printf("执行成功\n\n");
}
}
mysql_free_result(result);
}
几个常用的函数封装好了,感觉有点冗余。
更友好的界面
学习c primer plus第八章字符输入/输出和输入验证时,学到了缓冲区和简单的交互菜单设计,正好套上这里来了。
缓冲区
缓冲分为两类:完全缓冲I/O和行缓冲I/O。完全缓冲输入指的是当缓冲区被填满时才刷新缓冲区(内容被发送至目的地),通常出现在文件输入中。而键盘输入通常都是行缓冲输入,所以在按下Enter键后才刷新缓冲区。
缓冲输入要求用户按下Enter键发送输入,这一动作也传送了换行符,我们必须小心处理这个换行符。
int num=0;
printf("输入y打印num加一,输入n终止\n");
while(getchar()!='n')
{
printf("This is %d",num++);
}
printf("Stop");
第一次的行缓冲是 y\n ,逐个字符执行操作,'y’打印加一,'n’也打印加一,因此一次输入会重复两次打印加一。
最后一次输入的行缓冲是n\n,逐个字符执行操作,'n’就跳出循环了。一种解决方法就是跳过剩余的输入行
while(getchar())!='n')
{
printf("This is %d",num++);
while(getchar()!='\n')
continue;
}
若输入y,缓冲行为y\n,’\n’就不进行操作,被跳过了。
若输入yes,缓冲行为yes\n,只有’y’执行时打印了一次num+1,后面的都被跳过了。
登录界面
mysql_init(&mysql);
char host[MAX];
char user[MAX], passwd[MAX];
char db[] = "mysql";
//连接之前必须使用这个函数来初始化
/*声明登录数据库所需的参数*/
unsigned int port = 3306;
char* unix_socket =NULL;//如果unix_socket不是 NULL,则字符串指定要使用的套接字或命名管道。请注意,该 host参数确定连接的类型。
unsigned long client_flag = 0;//值client_flag通常为0,但可以将其设置为以下标志的组合以启用某些功能:见C API档案
printf("请输入主机名或ip地址,本机可直接回车或输入\"localhost\"。\n");
gets(host);
printf("请输入连接mysql的用户名:");
scanf("%s", user);
printf("请输入用户密码:");
inputpasswd(passwd);
while (getchar() != '\n') continue;//这行有作用,后面会讲到
printf("登录中,请稍等。\n");
if ((sock = mysql_real_connect(&mysql, host, user, passwd, db, port, unix_socket, client_flag)) != NULL) //连接MySQL
{
system("cls");
printf("连接MySQL成功!!\n");
}
else
{
system("cls");
printf("连接失败,原因是: \n");
fprintf(stderr, " %s\n", mysql_error(&mysql));
exit(1);
}
menu();//菜单界面
mysql_close(&mysql);
mysql_close(sock);
system("pause");
exit(EXIT_SUCCESS);
留意,最后用scanf()读取密码时,行缓冲还留着一个换行’\n’!
菜单设计
get_first()函数可以只读取行缓冲的第一个字符,后面的全部跳过。以此保证get_choice()都是单个字符,方便程序操作。
char get_first()
{
int ch;
ch = getchar();
while (getchar() != '\n') continue;
return ch;
}
char get_choice()
{
int ch;
printf("\n\n\n\n+------------------------------------------------+\n");
printf("|\t\t 欢迎菜单\t\t\t |\n");
printf("|\t\ta.展示所有数据库\t\t |\n");
printf("|\t\tb.展示当前数据库中表\t\t |\n");
printf("|\t\tc.创建数据库\t\t\t |\n");
printf("|\t\td.删除数据库\t\t\t |\n");
printf("|\t\te.选择数据库\t\t\t |\n");
printf("|\t\tf.自由sql指令增删改查\t\t |\n");
printf("|\t\tq.退出程序\t\t\t |\n");
printf("+------------------------------------------------+\n");
ch = get_first();
while (ch != 'q' && (ch < 'a' || ch>'f'))
{
printf("Please respond with 'a'to'f' or 'q'\n");
printf("Try again!\n");
ch = get_first();
}
return ch;
}
void menu()
{
int choice;
while ((choice = get_choice()) != 'q')
{
switch (choice)
{
case 'a':
showDB();
break;
case 'b':
showTables();
break;
case 'c':
createDB();
break;
case 'd':
deleteDB();
break;
case 'e':
selectDB();
break;
case 'f':
queryOperate();
break;
default:
printf("程序出错啦!:\n");
break;
}
}
printf("bye.");
system("pause");
mysql_close(&mysql);
exit(0);
}
小心scanf()和getchar()混用
留意,最后用scanf()读取密码时,行缓冲还留着一个换行’\n’!
menu()里get_choice()调用了get_first(),读取到了上次行缓冲没读完的’\n’,在get_choice()判断后打印了这行信息。
有两个解决方法,一是把scanf()后的换行符读完。
二是重写get_first()让他返回下一个字符。
char get_first()
{
int ch;
ch = getchar();
if (ch == '\n')
ch = getchar();
while (getchar() != '\n') continue;
return ch;
}
输入密码显示*号
这个是输入密码显示 *** ,能退格的函数。
/* 输入密码函数
getch()是conio.h中的函数,他与getchar()不同在于,不会立刻显示到屏幕中。
*/
#include <stdio.h>
#include <conio.h>
void inputpasswd(char *password)
{
char ch;
int index = 0;
while ((ch = getch()) != '\r') // \r是回车
{
if (ch == 8 && index > 0)//退格的ASCII码是8
{
index--;
password[index] = '\0';
printf("\b \b");/*先退格,空格覆盖当前光标的下一个星号,再退格输入*/
}
if (index < MAX - 1 && ch != 8)
{
password[index++] = ch;
putchar('*');
}
}
password[index] = '\0';
printf("\n");
}
修复一些bug
这里中文显示乱码,于是在main函数里设置了一下客户端编码为gbk兼容中文
if (mysql_query(&mysql, "set names gbk"))
{
printf("set names gbk 失败:%s", mysql_error(&mysql));
mysql_close(&mysql);
exit(-1);
}
全代码
大功告成,成功实现了要求的全部功能。下面给出全代码。
#define _CRT_SECURE_NO_WARNINGS
#define MAX 20
#include <windows.h>
#include "stdio.h"
#include <conio.h>
#include <string.h>
#include "mysql.h"
MYSQL mysql, * sock; //声明MYSQL*连接处理程序 sock指socket
MYSQL_RES* result; //查询结果集
MYSQL_ROW row; //代表的是结果集中的一行
MYSQL_FIELD* field; //包含字段信息的结构指针
unsigned int num_fields;//表的列数
unsigned int num_rows;//表的行数
void showDB();
void showTables();
void createDB();
void deleteDB();
void selectDB();
void queryOperate();
void inputpasswd(char* password);
char get_first(void);//用获得第一个字母的方法更好
char get_choice(void);
void menu();
int main(void)
{
mysql_init(&mysql);
char host[MAX];
char user[MAX], passwd[MAX];
char db[] = "mysql";
//连接之前必须使用这个函数来初始化
/*声明登录数据库所需的参数*/
unsigned int port = 3306;
char* unix_socket =NULL;//如果unix_socket不是 NULL,则字符串指定要使用的套接字或命名管道。请注意,该 host参数确定连接的类型。
unsigned long client_flag = 0;//值client_flag通常为0,但可以将其设置为以下标志的组合以启用某些功能:见C API档案
printf("请输入主机名或ip地址,本机可直接回车或输入\"localhost\"。\n");
gets(host);
printf("请输入连接mysql的用户名:");
scanf("%s", user);
printf("请输入用户密码:");
inputpasswd(passwd);
while (getchar() != '\n') continue;
printf("登录中,请稍等。\n");
if ((sock = mysql_real_connect(&mysql, host, user, passwd, db, port, unix_socket, client_flag)) != NULL) //连接MySQL
{
system("cls");
printf("连接MySQL成功!!\n");
}
else
{
system("cls");
printf("连接失败,原因是: \n");
fprintf(stderr, " %s\n", mysql_error(&mysql));
exit(1);
}
if (mysql_query(&mysql, "set names gbk")) {
printf("set names gbk 失败:%s", mysql_error(&mysql));
mysql_close(&mysql);
exit(-1);
}
menu();
mysql_close(&mysql);
mysql_close(sock);
system("pause");
exit(EXIT_SUCCESS);
}
void inputpasswd(char *password)
{
char ch;
int index = 0;
while ((ch = _getch()) != '\r')
{
if (ch == 8 && index > 0)//退格的ASCII码是8
{
index--;
password[index] = '\0';
printf("\b \b");/*空格覆盖下一个星号,再退格输入*/
}
if (index < MAX - 1 && ch != 8)
{
password[index++] = ch;
putchar('*');
}
}
password[index] = '\0';
printf("\n");
}
char get_first()
{
int ch;
ch = getchar();
while (getchar() != '\n') continue;
return ch;
}
char get_choice()
{
int ch;
printf("\n\n\n\n+------------------------------------------------+\n");
printf("|\t\t 欢迎菜单\t\t\t |\n");
printf("|\t\ta.展示所有数据库\t\t |\n");
printf("|\t\tb.展示当前数据库中表\t\t |\n");
printf("|\t\tc.创建数据库\t\t\t |\n");
printf("|\t\td.删除数据库\t\t\t |\n");
printf("|\t\te.选择数据库\t\t\t |\n");
printf("|\t\tf.自由sql指令增删改查\t\t |\n");
printf("|\t\tq.退出程序\t\t\t |\n");
printf("+------------------------------------------------+\n");
ch = get_first();
while (ch != 'q' && (ch < 'a' || ch>'f'))
{
printf("Please respond with 'a'to'd' or 'q'");
printf("Try again!\n");
ch = get_first();
}
return ch;
}
void menu()
{
int choice;
while ((choice = get_choice()) != 'q')
{
switch (choice)
{
case 'a':
showDB();
break;
case 'b':
showTables();
break;
case 'c':
createDB();
break;
case 'd':
deleteDB();
break;
case 'e':
selectDB();
break;
case 'f':
queryOperate();
break;
default:
printf("程序出错啦!:\n");
break;
}
}
printf("bye.");
system("pause");
mysql_close(&mysql);
exit(0);
}
void showDB()
{
if (mysql_query(&mysql, "show databases"))
{
printf("mysql_query failed.\n");
}
result = mysql_store_result(&mysql);
if (result)//有返回结果集
{
num_fields = mysql_num_fields(result);
printf("Affacted %lu rows.\n\n", (unsigned long)mysql_affected_rows(&mysql));
for (int i = 0; field = mysql_fetch_field(result); i++)
{
//获得属性名
printf("%24s", field->name);
printf(" |");
}
printf("\n-------------------------|\n");
while (row = mysql_fetch_row(result))
{
for (int i = 0; i < num_fields; i++)
{
printf("%25s", row[i]);
printf("|");
}
printf("\n");
}
}
mysql_free_result(result);
}
void showTables()
{
if (mysql_query(&mysql, "show tables"))
{
printf("mysql_query failed.\n");
}
result = mysql_store_result(&mysql);
if (result)//有返回结果集
{
num_fields = mysql_num_fields(result);
printf("Affacted %lu rows.\n\n", (unsigned long)mysql_affected_rows(&mysql));
for (int i = 0; field = mysql_fetch_field(result); i++)
{
//获得属性名
printf("%24s", field->name);
printf(" |");
}
printf("\n-------------------------|\n");
while (row = mysql_fetch_row(result))
{
for (int i = 0; i < num_fields; i++)
{
printf("%25s", row[i]);
printf("|");
}
printf("\n");
}
}
mysql_free_result(result);
}
void createDB()
{
char query[80] = "create database ";
char dbName[20];
printf("输入要创建的数据库名:");
gets(dbName);
strcat(query, dbName);
if (!mysql_query(&mysql, query))
{
printf("创建成功\n");
}
else
{
if (mysql_errno(&mysql))//出错
{
printf("创建失败。\n");
fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
}
}
}
void deleteDB()
{
char query[80] = "drop database ";
char dbName[20];
printf("输入要删除的数据库名: ");
gets(dbName);
strcat(query, dbName);
if (!mysql_query(&mysql, query))
printf("删除成功。\n");
else
{
if (mysql_errno(&mysql))//出错
{
printf("删除失败。\n");
fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
}
}
}
void selectDB()
{
char query[80] = "use ";
char dbName[20];
printf("输入要使用(use)的数据库名: ");
gets(dbName);
strcat(query, dbName);
if (!mysql_query(&mysql, query))
printf("正在使用%s。\n",dbName);
else
{
if (mysql_errno(&mysql))//出错
{
printf("操作失败。\n");
fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
}
}
}
void queryOperate()
{
char query[80];
printf("输入SQL语句:");
gets(query);
if (mysql_query(&mysql, query))
{
printf("mysql_query failed.\n");
}
result = mysql_store_result(&mysql);
if (result) //有返回结果集
{
num_fields = mysql_num_fields(result);
printf("Affacted %lu rows.\n\n", (unsigned long)mysql_affected_rows(&mysql));
while (field = mysql_fetch_field(result))
{
printf("%24s",field->name);
printf(" |");//获得属性名
}
printf("\n");
while (row = mysql_fetch_row(result))
{
for (int i = 0; i < num_fields; i++)
{
printf("%25s", row[i]);
printf("|");
}
printf("\n");
}
}
else //结果集是NULL没有数据
{
if (mysql_errno(&mysql))//连接出错
{
fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
}
else if (mysql_field_count(&mysql) == 0)//
{
// query does not return data
// (it was not a SELECT)
printf("Affacted %lu rows.\n", (unsigned long)mysql_affected_rows(&mysql));
printf("执行成功\n\n");
}
}
mysql_free_result(result);
}
至此,这篇文章结束啦!自己敲完代码运行成功已经很爽了。从头来过解构建构出个记录教程,更进一步掌握了知识,真的比刚开始完成任务还要high好几倍。
感谢你的阅读!写博客不易,还请多支持!希望这篇文章能帮到你!