[寒假作业]C连接MySQL

C语言连接Mysql数据库

本小白写的第一个博客,请多关照!
一路上太多坑了,我把我遇到了的觉得有价值的坑都记录下来,既是自己的记录成长,也方便了以后的广大小伙伴。 [全文较长 建议打开文章目录食用]
这次目标具体如图,
在这里插入图片描述
一下还不太好下手有点懵哈,不管怎样,先学起来,在最大的视频学习网站上找到了不错的mysql数据库教程,戳这里看看

我的操作过程大概分下面几步

  1. MySQL学习与软件安装
  2. MySQl配置问题
  3. C连接MySQL配置
  4. 编写函数体、代码
  5. 修复一些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时,有两种情况
  1. 只有dll而没有.h和.lib而有api文档的情况下,可以通过LoadLibrary动态加载dll,并通过GetProcAddress来获取函数地址并使用。
  2. 有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好几倍。

感谢你的阅读!写博客不易,还请多支持!希望这篇文章能帮到你!

发布了2 篇原创文章 · 获赞 3 · 访问量 1409

猜你喜欢

转载自blog.csdn.net/weixin_45692835/article/details/104161320