首先交代一下我的 MySQL v8.0.13 是64位的,在写这个东西之前在网上查了相关的资料,如果调试的时候采用不同的位数,那么可能引发一些错误,为了保证过程尽量顺利,我在 VS2017 上写的时候我的“解决方案平台”是选择了 X64 。
看过别人博客说,头文件的顺序位置也有讲究,特别是 winsock.h 和 mysql.h 的顺序讲究,我是这样写的:
#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
#include <winsock.h>
#include <mysql.h>
#include <string.h>
#pragma comment (lib, "libmysql.lib")
可以看到这里多了一个 libmysql.lib 的静态库,虽然接下来一些方法来自 mysql.h ,但这个 lib 也是必不可少的,如果已经安装了MySQL,那么在MySQL的目录下会有两个文件夹分别是 include 和 lib ,其中 mysql.h 文件就是在这个 include 文件夹里了。接下来,在项目属性页内将 include 这个文件夹放到包含目录,lib 文件夹放到库目录让 VS “有迹可循”,如图:
然而文件的引入部分还没有结束, 我还要把 MySQL 目录下 lib 文件夹里的一个 libmysql.dll 文件进行复制,然后把这个文件粘贴到路径 C:\Windows\System32 下和该项目的 .exe 同一个路径下(如我的就是 E:\案例库\ExampleDatabase\x64\Debug )。
然后可以疯狂恁代码了,其中MySQL的官方文档提供了很多关于API的介绍,如果想要程序有更多功能详情查询官方文档《MySQL 8.0 Reference Manual Including MySQL NDB Cluster 8.0》 Character 28 中的 28.7 MySQL C API
完整代码如下:
#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
#include <winsock.h>
#include <mysql.h>
#include <string.h>
#pragma comment (lib, "libmysql.lib")
int main() {
//int yon;
char yon[1];
int YesOrNo = 1;
MYSQL mysqlConnect; //数据源指针
MYSQL_RES *res; //查询结果集
MYSQL_FIELD *field; //包含字段信息的结构指针
MYSQL_ROW nextRow; //存放查询sql语句字符串数组
int ret; //执行sql语句后返回是否成功查询
int i, j;
mysql_init(&mysqlConnect);//分配对象 p4302
if (!(mysql_real_connect(&mysqlConnect, "localhost", "root", "1234", "exampledb", 3306, NULL, 0))) {
printf("Failed to access to the database...Error: %s\n", mysql_error(&mysqlConnect));
}
if (!mysql_set_character_set(&mysqlConnect, "gbk"))
{
printf("New client character set: %s\n",
mysql_character_set_name(&mysqlConnect));
}
while (YesOrNo == 1)
{
YesOrNo = 3; //令他不等于1就好
printf("Now you can input the keyword to find anything what you hope to see.\n");
char keyword[100];
scanf("%s", keyword); //要查询的信息关键词
printf("\n");
char toSelectFrom[250] = "SELECT * FROM core_courses WHERE "; //where后保留空格好直接连接关键词
char toLike[] = " LIKE '%"; //等号前后保留空格
char rightPercent[] = "%'";
char or [] = " OR ";
char number[] = "course_number";
char name[] = "course_name";
char credit[] = "credit";
char period[] = "period";
char term[] = "term";
char type_1[] = "course_type_1";
char type_2[] = "course_type_2";
//下面开始字符串拼接
char thePerfectSqlLanguage[254];
strcat(toSelectFrom, number); /*SELECT * FROM core_courses WHERE course_number*/
strcat(toSelectFrom, toLike); /*SELECT * FROM core_courses WHERE course_number = */
strcat(toSelectFrom, keyword); /*SELECT * FROM core_courses WHERE course_number = keyword*/
strcat(toSelectFrom, rightPercent);
strcat(toSelectFrom, or ); /*SELECT * FROM core_courses WHERE course_number = keyword OR */
strcat(toSelectFrom, name);
strcat(toSelectFrom, toLike);
strcat(toSelectFrom, keyword);
strcat(toSelectFrom, rightPercent);
strcat(toSelectFrom, or );
strcat(toSelectFrom, credit);
strcat(toSelectFrom, toLike);
strcat(toSelectFrom, keyword);
strcat(toSelectFrom, rightPercent);
strcat(toSelectFrom, or );
strcat(toSelectFrom, period);
strcat(toSelectFrom, toLike);
strcat(toSelectFrom, keyword);
strcat(toSelectFrom, rightPercent);
strcat(toSelectFrom, or );
strcat(toSelectFrom, term);
strcat(toSelectFrom, toLike);
strcat(toSelectFrom, keyword);
strcat(toSelectFrom, rightPercent);
strcat(toSelectFrom, or );
strcat(toSelectFrom, type_1);
strcat(toSelectFrom, toLike);
strcat(toSelectFrom, keyword);
strcat(toSelectFrom, rightPercent);
strcat(toSelectFrom, or );
strcat(toSelectFrom, type_1);
strcat(toSelectFrom, toLike);
strcat(toSelectFrom, keyword);
strcat(toSelectFrom, rightPercent);
//下面把完整的SQL查询语句 复制 给 thePerfectSqlLanguage
strcpy(thePerfectSqlLanguage, toSelectFrom);
ret = mysql_query(&mysqlConnect, thePerfectSqlLanguage); //执行
if (ret != 0) {
printf("Query failed...Error: %s\n", mysql_error(&mysqlConnect));
//mysql_close(&mysqlConnect); //关闭连接
//continue; //退出系统
/*考虑加个模糊查询*/
}
res = mysql_store_result(&mysqlConnect);
if (res) {
int fieldCount = mysql_field_count(&mysqlConnect);
if (fieldCount > 0) {
int column = mysql_num_fields(res);
int row = mysql_num_rows(res);
for (i = 0; field = mysql_fetch_field(res); i++) {
//获得属性名
printf("%25s", field->name);
printf(" |");
}
printf("\n");
//按行输出结果
/*for (i = 1; i < row + 1; i++) {
rowList = mysql_fetch_row(res); //4273
for (j = 0; j < column; j++) {
printf("%10s", rowList[j]);
}
printf("\n");
}*/
while (nextRow = mysql_fetch_row(res)) {
for (j = 0; j < column; j++) {
printf("%25s", nextRow[j]);
printf(" |");
}
printf("\n");
}
}
else {
printf("No resullt. This is the result of a character splitting query... \n");
}
}
else {
printf("mysql_store_result...Error: %s\n", mysql_error(&mysqlConnect));
}
printf("\n\nIf you still hope to access the database, please input '1' (Stay) or '0' (Exit).\n");
/*scanf("%d", &yon);
while (yon != 0 && yon != 1) {
printf("Warning: You have input the number out of my expection! Please enter '1'(Continue to access the database) or '0'(Exit now) now.\n");
scanf("%d", &yon);
}
if (yon == 1) {
YesOrNo = yon;
}
else if(yon==0) {
exit(1);
}*/
/*之所以不采用上面这种写法是应为当输入非数字类型时会出错*/
printf("\n\n");
scanf("%s", yon); /*如果这里采用getchar那么在回车的一瞬间弹出warning*/
while (yon[0] != '0' && yon[0] != '1') {
printf("Warning: You have input the number out of my expection! Please enter '1'(Continue to access the database) or '0'(Exit now) now.\n");
scanf("%s", yon);
}
if (yon[0] == '1') {
YesOrNo = 1;
}
else if (yon[0] == '0') {
exit(1);
}
}
return 0;
}
写到这又有个要注意问题,原来 MySQL 的 utf8 并不是真正的 utf8,当初 sql 查询语句正确,但就是出不来结果,后来检查的时候发现查中文不行查数字可以,也就是说通过输入数字查询出来对应的结果集的一些中文属性都乱了,既然中文属性都乱了,那么通过查询中文肯定出不来正常的东西,后来查了相关资料,我自己把我所要查询的数据库编码改为 utf8mb4 (不禁得吐槽 utf8 家族真乱...),在数据库端新建查询:
--更改数据库编码
ALTER DATABASE exampledb CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
--更改表编码
ALTER TABLE core_course CONVERT TO CHARACTER SET utf8mb4 COLLATEutf8mb4_general_ci;
然后在我上面的这份 C 代码里自此补了这么一段:
if (!mysql_set_character_set(&mysqlConnect, "gbk"))
{
printf("New client character set: %s\n",
mysql_character_set_name(&mysqlConnect));
}
以此来告诉我这个客户端是 gbk 编码,和你数据库端的 utf8 编码不一样(应该是这个表达)。至此查询出乱码的问题得以解决。
运行结果:
补一些踩过的坑,日后免得又踩一次:
1. 我开头创建一个 MySQL 对象分别用了一个变量名 mysqlConnect 和 一个指针 *sock ,后面的一些 mysql 方法内如果填入指针他是会报错的,说你并没有初始化,好久没写 C 了,也不应该这么写。
2. 有时候看似要处理简简单单的一个输入也得考虑用户可能不小心非法输入导致程序崩溃,程序的健壮性也是能做做文章的。
3. 一个程序的可读性非常重要,就像写这个查询数据库的程序,这个过程是要经过重重验证的,有验证就可能返回不同的结果,也就是说,如果 if-else 循环里还有多个 if-else 循环,那么读起来的时候会因为层层嵌套而大大降低了代码的可读性,也不好维护,对此可以参考官方文档内的一些实例采用 if( ! ...... ) { ...... } 的写法来避免这种层层循环。
4. 另外,mysql_store_result() 这个方法对于 “返回空的结果集” 和 “返回空指针” 还是有点区别的,比如后者对于 insert 语句。
5. mysql_fetch_field() 指向当前列,然而 mysql_fetch_row 却是指向结果集下一行,这就想起了 Java 还要搞个游标这么麻烦。