20242817李臻《Linux⾼级编程实践》第9周
一、AI对学习内容的总结
第十章 Linux下的数据库编程
10.1 MySQL数据库简介
- MySQL概述:MySQL是一个开源的关系型数据库管理系统,最初由瑞典MySQL AB公司开发,后经SUN公司收购,现属于Oracle公司。它以其体积小、速度快、成本低等优势,成为中小型网站常用的数据库选择。
- MySQL的特点:
- 支持多线程,提供完全的多线程运行模式。
- 提供了面向多种编程语言的APIs,如C、C++、Java、Perl、PHP、Python等。
- 支持多种字段类型和完整的操作符支持。
- MySQL的应用:在WEB应用中,MySQL是目前最好的RDBMS应用软件之一,搭配PHP和Apache可组成良好的开发环境。
10.1.1 Linux数据库编程应用
- Web服务器中的应用:在Linux操作系统作为Web服务器时,用户的相关信息如账户信息、购买商品信息等需要保存在服务器中,服务器端程序可通过C语言将这些信息保存到数据库服务器中,也可读取保存在其中的信息并返回给Web客户端。
- 嵌入式领域的应用:在嵌入式Linux操作系统中,开发板经常需要将外部设备如各种传感器感知的数据保存到数据库中,为了提高效率,一般采用C语言直接对嵌入式数据库(例如SQLite等)进行操作。
- 网络环境中的应用:为了高效率地采集网络设备的数据,经常采用C语言在Linux环境下操作,通过调用数据库的API实现数据的各种DDL操作。
10.1.2 MySQL API的两种形式
- libmysqlclient:客户端程序和数据库服务器处于独立的不同进程中,客户端通过网络与数据库服务器进行通信。
- libmysqld:数据库服务器嵌入在客户端应用程序中,作为应用程序的一部分,客户端与数据库服务器端的通信通过私有进程的方式进行。该方式优点是用户程序和数据库紧耦合在一起,方便应用的部署和提高效率,降低维护成本,终端用户无需关心mysql数据库。缺点是在同一时刻只能有一个程序连接该数据库。
10.1.3 MySQL CAPI的使用
- 安装方式:可通过安装MySQL服务器版或MySQL Connector/C发布版来获得MySQL CAPI。
- 编译时的注意事项:
- 在编译命令中通过-I选项指定头文件的位置。
- 通过-lmysqlclient -lz选项告诉编译器使用mysql的库文件。
- 通过-L选项指定库文件的查找路径。
10.2 Linux数据库编程基本方法
- 基本步骤:
- 定义并初始化与数据库连接和操作相关的数据结构变量。
- 建立到数据库的连接。
- 执行相应的DDL操作,如select/delete/insert/update等语句。
- 关闭数据库连接释放资源。
10.3 MySQL数据库数据结构及API
10.3.1 数据结构
- MYSQL:代表到数据库的一个连接,几乎所有MySQL操作函数都用到该数据结构。
- MYSQL_RES:代表数据库操作返回的结果集。
- MYSQL_ROW:代表数据表中的一行数据。
- MYSQL_FIELD:包括数据表中字段的元数据信息,如字段名称、字段类型、字段大小等。
- MYSQL_FIELD_OFFSET:代表字段集中的偏移量。
- my_ulonglong:用来表示数据表中行数的字段。
- my_bool:MySQL中的布尔类型。
10.3.2 MySQL操作API
- mysql_init:初始化一个MySQL结构。
- mysql_library_init:初始化MySQL CAPI。
- mysql_real_connect:连接到指定的MySQL服务器。
- mysql_query:执行一个以空字符结尾的SQL查询字符串。
- mysql_real_query:执行一个可被计数的SQL查询字符串。
- mysql_store_result:返回SQL语句执行结果的结果集给客户。
- mysql_free_result:释放结果集使用的内存。
- mysql_affected_rows:返回执行SQL查询语句影响的行数。
- mysql_num_rows:返回一个结果集中行的数量。
- mysql_num_fields:返回一个结果集中列的数量。
- mysql_fetch_row:从结果集中取得下一行。
- mysql_fetch_lengths:返回当前行中所有列的长度。
- mysql_fetch_fields:返回所有字段结构的数组。
- mysql_get_server_info:返回服务器信息。
- mysql_get_client_info:返回客户端信息。
- mysql_get_host_info:返回描述连接信息。
- mysql_fetch_field:返回表的下一个字段的类型。
- mysql_fetch_field_direct:返回字段编号所指定的字段的类型。
- mysql_errno:返回最近被调用的MySQL函数的出错编号。
- mysql_error:返回最近被调用的MySQL函数的出错消息。
- mysql_close:关闭到数据库服务器连接。
- mysql_change_user:改变在一个打开的连接上的用户和数据库。
- mysql_create_db:创建一个数据库(不推荐使用该函数,使用CREATE语句)。
- mysql_drop_db:删除一个数据库(不推荐使用该函数,使用DROP语句)。
- mysql_select_db:选择服务器中某个数据库。
- mysql_thread_id:返回当前线程的ID。
- mysql_kill:杀死一个给定的线程。
- mysql_list_dbs:返回与指定的正则表达式匹配的数据库名。
- mysql_list_tables:返回与指定的正则表达式匹配的数据表名。
- mysql_list_fields:返回与指定的正则表达式匹配的列名。
- mysql_commit:提交本次事务。
- mysql_rollback:退回本次事务。
- mysql_shutdown:关闭数据库服务器。
10.4 MySQL数据库编程实例
- 学生信息管理系统实例:
- 数据库设计:创建名为student的数据库,建立保存学生基本信息的表格stuinfo,包含学号、姓名、性别、邮箱、院系等字段。
- 实例代码:提供了使用MySQL C API实现数据库创建、数据表创建、记录插入、记录删除和记录查询等操作的完整代码示例。
实验部分
-
实验一 Linux基本命令使用:
- 实验目的:了解Linux环境,熟悉Linux常用命令和VI编辑器的使用。
- 实验内容:包括Linux基本命令、VI编辑器的使用等。
-
实验二 Linux Shell编程:
- 实验目的:熟悉Shell环境、语法和语句,了解Shell程序的调试方法。
- 实验内容:编写和调试Shell程序,完成特定任务。
-
实验三 Makefile实验:
- 实验目的:了解Linux环境,熟悉Makefile的原理和使用。
- 实验内容:编写Makefile文件,使用make命令进行编译。
-
实验四 GCC/GDB实验:
- 实验目的:了解Linux的编译调试环境,熟悉GCC和GDB的使用。
- 实验内容:使用GCC编译程序,使用GDB进行调试,创建和使用静态库和动态库。
-
实验五 Linux文件系统编程:
- 实验目的:了解系统编程概念,熟悉Linux系统编程方法和常用的系统调用。
- 实验内容:编写程序实现文件系统调用和信号处理。
-
实验六 Linux多进程与进程间通信:
- 实验目的:熟悉Linux系统编程方法和常用的系统调用。
- 实验内容:编写程序实现多进程创建和进程间通信。
二、 对AI总结的反思与补充
反思
10.1.2 MySQL API 的两种形式
- libmysqlclient :是常见的客户端连接库,适用于客户端与 MySQL 服务器的独立进程通信。
- libmysqld :是 MySQL 内嵌式服务器库,可以将 MySQL 数据库嵌入到应用程序中,通常用于在没有独立 MySQL 服务器的情况下运行数据库。不过,libmysqld 已不再是主流选择,在现代的 MySQL 版本中已经逐渐不被推荐或被淘汰。
10.1.3 MySQL CAPI 的使用
在安装方式上,可以通过 “安装 MySQL 服务器版或 MySQL Connector/C 发布版来获得 MySQL CAPI” 这一描述是正确的。而更明确的说法是:MySQL Connector/C 是 MySQL 提供的官方 C 语言连接器库,它包含了 MySQL C API。
10.3 MySQL 数据库数据结构及 API
-
mysql_real_query 和 mysql_query
- mysql_query :是一个比较基础的 API,用于执行 SQL 查询。
- mysql_real_query :与 mysql_query 功能相似,但它提供了对字符集的更好支持,例如能够处理多字节字符集。对于简单查询,通常使用 mysql_query而对于;更复杂的查询,特别是涉及字符集问题时,推荐使用 mysql_real_query。
-
mysql_list_dbs, mysql_list_tables 和 mysql_list_fields :这些函数在 MySQL 的新版本中已经不推荐使用,并且被标记为过时(deprecated)。现在推荐使用 SHOW DATABASES、SHOW TABLES 和 SHOW COLUMNS 来替代这些函数。
10.4 MySQL 数据库编程实例
- 学生信息管理系统实例 :
- 在数据库设计部分,可以进一步详细描述,比如提供表格 stuinfo 的创建 SQL 语句,以及详细阐述如何通过 C 语言实现增删改查等操作,以便让读者更好地理解如何实际操作数据库。
补充
10.1 MySQL数据库简介
- MySQL的事务支持:MySQL支持ACID(原子性、一致性、隔离性、持久性)事务,确保在数据库操作中的数据一致性和可靠性。常用的事务控制命令包括
START TRANSACTION
、COMMIT
和ROLLBACK
。 - MySQL的存储引擎:MySQL提供多个存储引擎,每个引擎在性能、功能等方面有所不同。常见的存储引擎有
InnoDB
(支持事务、外键)和MyISAM
(不支持事务、外键,但速度更快)。 - MySQL的复制功能:MySQL支持主从复制,可以将数据从一个主数据库复制到多个从数据库,增强数据的可用性和容错性。
10.2 Linux数据库编程基本方法
- MySQL连接池:对于高并发的Web应用,频繁的创建和销毁数据库连接会导致性能问题。使用数据库连接池可以复用现有连接,提高效率。
- 错误处理:在数据库编程中,需要特别注意错误处理。可以使用
mysql_errno()
和mysql_error()
获取详细的错误信息,并根据错误代码处理异常,保证系统的健壮性。 - SQL注入防护:SQL注入是Web应用中常见的安全漏洞之一。在进行数据库操作时,建议使用准备语句(Prepared Statements)来防止SQL注入,MySQL C API也支持该特性。
10.3 MySQL数据库数据结构及API
MYSQL_RES
与MYSQL_ROW
:MYSQL_RES
是SQL查询结果的容器,包含一个或多个MYSQL_ROW
。一个MYSQL_ROW
就是数据库查询结果中的一行数据,它通常以一个字符串数组的形式返回。- 批量处理与事务:对于大规模数据的插入操作,MySQL C API可以通过开启事务(
mysql_query("START TRANSACTION")
)来提高效率。在执行完一批插入后,通过COMMIT
提交事务,从而减少了单次插入的网络延迟。 - MySQL的错误处理:除了
mysql_errno()
和mysql_error()
外,还可以通过mysql_get_client_version()
获取客户端库的版本信息,进行调试时非常有帮助。
10.4 MySQL数据库编程实例
- 代码优化与索引使用:在设计数据库时,合理的索引可以极大提高查询性能。尤其是在进行复杂查询或大量数据处理时,创建合适的索引至关重要。编写代码时,可以使用
EXPLAIN
命令来分析SQL语句的执行计划,从而优化数据库查询效率。 - Prepared Statements:在编写C语言与MySQL交互时,使用准备语句不仅能防止SQL注入,还能提高查询效率。MySQL C API提供了
mysql_stmt_prepare
和mysql_stmt_execute
等API,用于执行准备语句。
三、学习思维导图
第十章
mindmap
root((Linux下的数据库编程))
MySQL数据库简介
基本信息
开发背景
所属公司
社区版与商业版
特点
体积小
速度快
成本低
开源
应用场景
Web应用
嵌入式开发
Linux数据库编程应用
Web服务器
账户信息
购买信息
嵌入式Linux
传感器数据
网络数据采集
C语言操作
MySQL C API
两种形式
libmysqlclient
独立进程
libmysqld
嵌入式
紧耦合
使用方法
安装MySQL服务器版
安装MySQL Connector/C
编译链接
头文件
库文件
Linux数据库编程基本方法
数据库连接
初始化
连接
数据库操作
DDL操作
DML操作
资源释放
关闭连接
释放内存
MySQL数据库数据结构及API
数据结构
MYSQL
MYSQL_RES
MYSQL_ROW
MYSQL_FIELD
my_ulonglong
my_bool
API函数
初始化与结束
数据库连接
查询与结果处理
错误处理
事务处理
MySQL数据库编程实例
学生信息管理系统
数据库设计
数据库:student
表:stuinfo
功能实现
数据库创建
数据表创建
数据插入
数据删除
数据查询
实验与练习
Linux基本命令使用
Shell编程
Makefile实验
GCC/GDB实验
文件系统编程
多进程与进程间通信
四、基于AI的学习
1、关于 MySQL API
我:我在学习 MySQL 操作 API 相关知识点,请你以苏格拉底的方式对我进行提问,一次一个问题。
AI:好的,开始啦。请你先说说,你对 MySQL 操作 API 目前有了哪些了解呢?
我:MySQL 操作 API 是用来和 MySQL 数据库进行交互的工具,不同的编程语言都有对应的 API,像 Python 有 PyMySQL,Java 有 JDBC 等,能让开发者在代码里执行 SQL 语句,完成对数据库数据的增删改查操作。
AI:明白了,那你觉得使用 MySQL 操作 API 最关键的步骤有哪些呢?
我:首先是建立数据库连接,要提供正确的主机地址、端口、用户名、密码等信息。然后是创建操作数据库的游标对象,接着执行 SQL 语句,无论是查询还是更新数据。如果是查询,要获取结果集并处理数据;最后记得关闭游标和连接,不然可能会浪费数据库资源。
AI:很棒呀,那请你思考一下,在执行 SQL 查询操作时,一般会用到哪些方法呢?
我:在 Python 的 PyMySQL 里,游标对象有 cursor.fetchall()、cursor.fetchone()、cursor.fetchmany(size) 这些方法。fetchall() 是获取查询结果集里的所有数据;fetchone() 获取下一行数据;fetchmany(size) 就是获取指定数量的多行数据。
AI:很好,那你知道在执行更新操作(比如插入、删除、修改数据)后,为什么一定要调用连接对象的 commit() 方法吗?
我:因为数据库默认是开启了事务的,如果不调用 commit() 方法,之前执行的更新操作不会真正地保存到数据库里,只有 commit 提交之后,数据变更才会生效,这样也能保证数据更新的完整性和一致性。
AI:非常棒!那在实际开发中,使用 MySQL 操作 API 时,如何有效处理异常情况呢?
我:在代码里要使用 try…except…finally 结构。在 try 块里执行数据库操作,捕获可能出现的异常,比如数据库连接错误、SQL 语句语法错误等。在 except 块里记录错误信息,进行相应的错误处理。在 finally 块里确保关闭游标和连接,防止资源泄漏。
AI:很好,请你思考一下,使用 MySQL 操作 API 时,如何避免 SQL 注入攻击呢?
我:不能直接拼接用户输入来构造 SQL 语句。要使用参数化查询,把用户输入当作参数传入,让 API 自动进行转义处理。像在 PyMySQL 中,执行 SQL 语句时用 %s 作为参数占位符,然后把参数以元组或字典形式传给 execute() 方法。
AI:不错呀,那你觉得在处理大量数据查询时,有什么比较好的优化方法呢?
我:一方面可以在 SQL 查询语句里加上合理的限制条件,比如利用 where 子句精准过滤数据,减少返回的数据量。另一方面可以对查询结果进行分页处理,每次只获取一部分数据展示,降低单次查询的压力。
AI:好了,关于 MySQL 操作 API 的知识点就交流到这里。你的理解是什么?
我:你觉得我掌握这些知识了吗?上述回答就是我的理解。
AI:从你的回答来看,你对 MySQL 操作 API 的基本使用流程、关键方法、异常处理、安全防护(避免 SQL 注入)以及查询优化策略等方面都有比较清晰的认识,能够准确地阐述各个要点,这说明你对这个知识点已经有了不错的掌握。
不过,可以进一步深入的地方是,对于不同编程语言的 MySQL 操作 API 在细节上可能存在的差异,以及一些高级特性(如事务的隔离级别设置在 API 中的具体操作)可以再深入研究一下。总之,你现在的理解已经很扎实啦,继续加油!
2、关于 Linux Shell编程
我:我在学习 Linux Shell 编程的相关知识点,请你以苏格拉底的方式对我进行提问,一次一个问题。
AI:好的,开始吧。那你想先和我分享一下你对 Linux Shell 编程的初步认知吗?
我:Linux Shell 就像是一个命令行界面的“翻译官”,我们日常敲入的各种指令都是通过它来跟操作系统交流的,它能帮我们快速地执行各种操作,像文件管理、程序运行这些。而且 Shell 脚本就像是把这些指令组合起来,变成一个个能自动完成特定任务的小程序。
AI:那你觉得编写一个简单的 Shell 脚本,必不可少的步骤都有哪些呢?
我:首先得用文本编辑器新建一个文件,比如用 vi 或 nano。然后在脚本开头写上 “#!/bin/bash”,这是告诉系统用哪个 shell 解释器来运行脚本。接着就是写具体的命令,实现我们想要的功能,写完保存后,还要用 “chmod +x 脚本名” 给脚本加上可执行权限,最后就能用 “./脚本名” 来运行啦。
AI:很好呀,那你知道在 Shell 脚本里,变量是怎么定义和使用的吗?
我:定义变量很简单,直接写 “变量名=值”,中间不能有空格。要是想引用变量,就在变量名前加个美元符号 “$”,像 “echo $变量名”,这样就能把变量的值取出来用到命令里。
AI:棒棒哒!那条件语句在 Shell 脚本中很常用,你来说说 “if…elif…else” 这个结构大概长什么样呀。
我:基本格式大概是这样:
if [ 条件1 ]; then
# 条件1成立时执行的命令
elif [ 条件2 ]; then
# 条件2成立时执行的命令
else
# 以上条件都不成立时执行的命令
fi
先判断条件 1,要是成立就执行对应部分;要是不成立就接着判断条件 2,以此类推,最后的 else 就是兜底的。
AI:不错哦,那循环结构也是编程里少不了的。在 Shell 脚本里,for 循环常用,那它的一般写法是什么呢?
我:
一种是像这样对固定列表循环:
for 变量 in 元素1 元素2 ... 元素N; do
# 循环体,能用 $变量获取当前元素
done
还有一种是结合文件或者数字范围,比如遍历文本文件的每一行,或者从 1 到 10 这样的数字区间。
AI:太棒啦!那你知道如何在 Shell 脚本里接收用户输入吗?
我:用 “read” 命令就行啦。比如 “read -p ‘请输入你的名字:’ name”,这样就能提示用户输入,输入的内容会存到变量 name 里,后面就能通过 “$name” 来用啦。
AI:很好,那在 Shell 编程中,函数是怎么定义和调用的呢?
我:定义函数就是写个函数名,然后加上括号,接着把函数体写在大括号里,像这样:
函数名 () {
# 函数体,写要执行的命令
}
调用的话就简单啦,直接写 “函数名” 就行,要是函数需要参数,调用的时候按顺序把参数写在后面就行,像 “函数名 参数1 参数2”。
AI:棒极啦!那你觉得在实际的 Shell 编程过程中,有什么容易出错或者需要注意的地方吗?
我:我觉得变量名一定要注意别和系统的关键字或者内置变量重名,不然容易出岔子。还有在写条件判断的时候,括号和空格得严格按照格式来,不然 Shell 解释器会报错。再就是脚本的权限,要是忘记加可执行权限,就老跑不起来啦。
AI:你的理解很到位呀,那你觉得你现在对 Linux Shell 编程的知识点掌握得怎么样啦?上面就是我的理解啦。
从你的回答来看,你对 Linux Shell 编程的基础概念、脚本编写流程、变量定义使用、条件语句、循环结构、用户输入接收以及函数定义调用等方面已经有比较扎实的理解,而且对实际操作中容易踩的坑也有一定警觉,这说明你对这个知识点有了不错的掌握程度啦。
不过呢,要是能再多探索一下 Shell 脚本和其他 Linux 工具结合的高级玩法,比如跟 find、xargs 这些命令搭配做批量文件处理,或者深入研究一下 Shell 中的重定向、管道在复杂场景下的巧妙运用,那你的 Shell 编程技能肯定会更上一层楼哦。继续加油,你已经很棒啦!
五、基于AI的代码实践
使用Gitee进行代码托管
仓库链接:
https://gitee.com/li-zhen1215/homework/tree/master/Week9
例1
ex1.c
#include "mysql.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student { // 对应于表的列属性
int id; // 学号
char name[20]; // 姓名
char sex[6]; // 性别
char mailbox[40]; // 邮箱地址
char studep[100]; // 所在院系
};
/*初始化数据库*/
MYSQL *initial(MYSQL *con) {
if (mysql_library_init(0, NULL, NULL)) {
fprintf(stderr, "Could not initialize MySQL library.\n");
return NULL;
}
con = mysql_init(NULL);
return con;
}
/*连接数据库*/
MYSQL *myconnect(MYSQL *con, char *username, char *passwd, char *hostip, char *db) {
if (mysql_real_connect(con, "localhost", "root", "123456", db, 0, NULL, 0) == NULL) {
fprintf(stderr, "connect failed\n");
return NULL;
}
return con;
}
void createDB(MYSQL *con, char *cmd) {
if (mysql_query(con, cmd)) {
printf("createdb failed");
} else {
printf("Create Database successful\n");
}
}
void createTable(MYSQL *con, char *db, char *cmd) {
if (mysql_select_db(con, db)) {
printf("select DB failed\n");
}
if (mysql_query(con, cmd)) {
printf("createTable failed\n");
} else {
printf("Create table successful\n");
}
}
void displayTable(MYSQL *con, char *query) {
MYSQL_RES *mysql_res;
MYSQL_ROW mysql_row;
unsigned long *lengths; // 声明 lengths 指针
int i, j;
int num_row, num_col;
if (mysql_real_query(con, query, (unsigned int)strlen(query)) != 0) {
printf("Query failed\n");
}
// 保存查询结果
if ((mysql_res = (MYSQL_RES *)mysql_store_result(con)) == NULL) { // 显式类型转换
printf("Store result failed\n");
}
// 获取查询结果的行数、列数
num_row = mysql_num_rows(mysql_res);
num_col = mysql_num_fields(mysql_res);
printf("row number = %lu, col number = %lu\n", (unsigned long)num_row, (unsigned long)num_col);
printf("********************\n");
// 逐行显示结果
for (i = 0; i < num_row; i++) {
// 从保存的查询结果中获取下一行内容
if ((mysql_row = mysql_fetch_row(mysql_res)) == NULL) { // 修正比较运算符
break;
}
// 获取列长度的数组
lengths = mysql_fetch_lengths(mysql_res); // 修正函数调用
for (j = 0; j < num_col; j++) {
printf("Column[%u] length=%lu\n", j, lengths[j]);
// 打印一行中各列的值
printf("%s\t", mysql_row[j] ? mysql_row[j] : "NULL");
}
printf("\n");
}
printf("********************\n");
// 释放保存的查询结果
mysql_free_result(mysql_res);
}
void insertData(MYSQL *con) {
struct student student1;
char query[1024];
printf("please input student id : \n");
scanf("%d", &student1.id);
getchar();
printf("please input student name:\n ");
scanf("%s", student1.name);
getchar();
printf("please input student sex(male/female):\n ");
scanf("%s", student1.sex);
getchar();
printf("please input student phone: \n");
scanf("%s", student1.mailbox);
getchar();
printf("please input student department: \n");
scanf("%s", student1.studep);
getchar();
sprintf(query, "insert into stuinfo(id, stuname, gender, mailbox, studep) values(%d, '%s', '%s', '%s', '%s')", student1.id, student1.name, student1.sex, student1.mailbox, student1.studep);
if (mysql_real_query(con, query, (unsigned int)strlen(query)) != 0) {
mysql_rollback(con);
printf("insert failed\n");
}
mysql_commit(con);
displayTable(con, "select * from stuinfo");
}
void deleteData(MYSQL *con) {
int id;
char query[1024];
printf("please input student id to be deleted\n");
scanf("%d", &id);
sprintf(query, "delete from stuinfo where id=%d", id);
if (mysql_real_query(con, query, (unsigned int)strlen(query)) != 0) {
mysql_rollback(con);
printf("delete failed\n");
}
mysql_commit(con);
if (mysql_affected_rows(con) > 0) {
printf("delete successful\n");
}
displayTable(con, "select * from stuinfo");
}
int menu() {
int choose;
printf("*********student information system*********\n");
printf("1. create database\n");
printf("2. create table\n");
printf("3. insert data\n");
printf("4. delete data\n");
printf("5. update data\n");
printf("please input choose\n");
scanf("%d", &choose);
getchar();
return choose;
}
void handleMenu(MYSQL *mysql) {
int choose;
while (1) {
choose = menu();
switch (choose) {
case 1: createDB(mysql, "Create database student"); break;
case 2: createTable(mysql, "student", "create table stuinfo(id INT, stuname TEXT, gender TEXT, mailbox TEXT, studep TEXT)"); break;
case 3: insertData(mysql); break;
case 4: deleteData(mysql); break;
case 5: // updateData(mysql); break;
case 0: printf("exit\n"); return; break;
}
}
}
int main(void) {
MYSQL *mysql_handle = NULL;
mysql_handle = initial(mysql_handle);
if (NULL == mysql_handle) {
fprintf(stderr, "initialize failed");
exit(1);
}
mysql_handle = myconnect(mysql_handle, "root", "123456", "localhost", "");
handleMenu(mysql_handle);
mysql_close(mysql_handle);
mysql_library_end();
return 0;
}
实践截图
习题1
Linux 下对 MySQL 进行 C 编程的主要应用场景和作用包括通过 MySQL 提供的 C API(如 libmysqlclient)实现高效的数据库操作,适用于需要直接与 MySQL 交互的高性能应用程序开发。这种编程方式常用于嵌入式系统、服务器后端或需要低层级控制的场景,例如定制数据库工具、批量数据处理或实时系统。通过 C 语言直接调用 MySQL,开发者可以优化查询性能、管理连接池或实现特定业务逻辑,同时避免其他语言可能带来的额外开销。此外,C 编程还能与 Linux 系统调用结合,实现更底层的资源管理或进程通信。
习题2
MySQL 的 C API(如 libmysqlclient)不仅支持基本的数据库操作(如查询、插入、更新和删除),还支持存储过程、事务管理、预处理语句等高级功能。对于存储过程,开发者可以通过 mysql_query() 或 mysql_real_query() 直接执行 CALL 语句来调用存储过程,并使用结果集处理函数(如 mysql_store_result() 或 mysql_use_result())获取返回数据。此外,MySQL C API 还支持预处理语句(Prepared Statements),通过 mysql_stmt_init()、mysql_stmt_prepare() 和 mysql_stmt_execute() 等函数高效执行参数化查询,减少 SQL 注入风险并提升性能。虽然 MySQL C API 主要针对 MySQL 设计,但通过 ODBC 或第三方库(如 Connector/C++ 或 DBI)可以间接支持其他数据库,但原生功能仍以 MySQL 为核心。
习题3
xt4.c
#include "mysql.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student { // 对应于表的列属性
int id; // 学号
char name[20]; // 姓名
char sex[6]; // 性别
char mailbox[40]; // 邮箱地址
char studep[100]; // 所在院系
};
struct score { // 成绩表结构体
int student_id; // 学号
char course[20]; // 课程名称
float grade; // 成绩
};
/*初始化数据库*/
MYSQL *initial(MYSQL *con) {
if (mysql_library_init(0, NULL, NULL)) {
fprintf(stderr, "Could not initialize MySQL library.\n");
return NULL;
}
con = mysql_init(NULL);
return con;
}
/*连接数据库*/
MYSQL *myconnect(MYSQL *con, char *username, char *passwd, char *hostip, char *db) {
if (mysql_real_connect(con, "localhost", "root", "123456", db, 0, NULL, 0) == NULL) {
fprintf(stderr, "connect failed\n");
return NULL;
}
return con;
}
void createDB(MYSQL *con, char *cmd) {
if (mysql_query(con, cmd)) {
printf("createdb failed");
} else {
printf("Create Database successful\n");
}
}
void createTable(MYSQL *con, char *db, char *cmd) {
if (mysql_select_db(con, db)) {
printf("select DB failed\n");
}
if (mysql_query(con, cmd)) {
printf("createTable failed\n");
} else {
printf("Create table successful\n");
}
}
void displayTable(MYSQL *con, char *query) {
MYSQL_RES *mysql_res;
MYSQL_ROW mysql_row;
unsigned long *lengths;
int i, j;
int num_row, num_col;
if (mysql_real_query(con, query, (unsigned int)strlen(query)) != 0) {
printf("Query failed\n");
}
if ((mysql_res = (MYSQL_RES *)mysql_store_result(con)) == NULL) {
printf("Store result failed\n");
}
num_row = mysql_num_rows(mysql_res);
num_col = mysql_num_fields(mysql_res);
printf("row number = %lu, col number = %lu\n", (unsigned long)num_row, (unsigned long)num_col);
printf("********************\n");
for (i = 0; i < num_row; i++) {
if ((mysql_row = mysql_fetch_row(mysql_res)) == NULL) {
break;
}
lengths = mysql_fetch_lengths(mysql_res);
for (j = 0; j < num_col; j++) {
printf("Column[%u] length=%lu\n", j, lengths[j]);
printf("%s\t", mysql_row[j] ? mysql_row[j] : "NULL");
}
printf("\n");
}
printf("********************\n");
mysql_free_result(mysql_res);
}
void insertData(MYSQL *con) {
struct student student1;
char query[1024];
printf("please input student id : \n");
scanf("%d", &student1.id);
getchar();
printf("please input student name:\n ");
scanf("%s", student1.name);
getchar();
printf("please input student sex(male/female):\n ");
scanf("%s", student1.sex);
getchar();
printf("please input student phone: \n");
scanf("%s", student1.mailbox);
getchar();
printf("please input student department: \n");
scanf("%s", student1.studep);
getchar();
sprintf(query, "insert into stuinfo(id, stuname, gender, mailbox, studep) values(%d, '%s', '%s', '%s', '%s')", student1.id, student1.name, student1.sex, student1.mailbox, student1.studep);
if (mysql_real_query(con, query, (unsigned int)strlen(query)) != 0) {
mysql_rollback(con);
printf("insert failed\n");
}
mysql_commit(con);
displayTable(con, "select * from stuinfo");
}
void deleteData(MYSQL *con) {
int id;
char query[1024];
printf("please input student id to be deleted\n");
scanf("%d", &id);
sprintf(query, "delete from stuinfo where id=%d", id);
if (mysql_real_query(con, query, (unsigned int)strlen(query)) != 0) {
mysql_rollback(con);
printf("delete failed\n");
}
mysql_commit(con);
if (mysql_affected_rows(con) > 0) {
printf("delete successful\n");
}
displayTable(con, "select * from stuinfo");
}
void insertScore(MYSQL *con) {
struct score score1;
char query[1024];
printf("please input student id : \n");
scanf("%d", &score1.student_id);
getchar();
printf("please input course name:\n ");
scanf("%s", score1.course);
getchar();
printf("please input grade:\n ");
scanf("%f", &score1.grade);
getchar();
sprintf(query, "insert into score(student_id, course, grade) values(%d, '%s', %f)", score1.student_id, score1.course, score1.grade);
if (mysql_real_query(con, query, (unsigned int)strlen(query)) != 0) {
mysql_rollback(con);
printf("insert score failed\n");
}
mysql_commit(con);
displayTable(con, "select * from score");
}
void displayScoreTable(MYSQL *con) {
displayTable(con, "select * from score");
}
void updateScore(MYSQL *con) {
int student_id;
char course[20];
float grade;
char query[1024];
printf("please input student id : \n");
scanf("%d", &student_id);
getchar();
printf("please input course name:\n ");
scanf("%s", course);
getchar();
printf("please input new grade:\n ");
scanf("%f", &grade);
getchar();
sprintf(query, "update score set grade=%f where student_id=%d and course='%s'", grade, student_id, course);
if (mysql_real_query(con, query, (unsigned int)strlen(query)) != 0) {
mysql_rollback(con);
printf("update score failed\n");
}
mysql_commit(con);
if (mysql_affected_rows(con) > 0) {
printf("update score successful\n");
}
displayTable(con, "select * from score");
}
void deleteScore(MYSQL *con) {
int student_id;
char course[20];
char query[1024];
printf("please input student id : \n");
scanf("%d", &student_id);
getchar();
printf("please input course name:\n ");
scanf("%s", course);
getchar();
sprintf(query, "delete from score where student_id=%d and course='%s'", student_id, course);
if (mysql_real_query(con, query, (unsigned int)strlen(query)) != 0) {
mysql_rollback(con);
printf("delete score failed\n");
}
mysql_commit(con);
if (mysql_affected_rows(con) > 0) {
printf("delete score successful\n");
}
displayTable(con, "select * from score");
}
void deleteStudentAndScore(MYSQL *con) {
int id;
char query[1024];
printf("please input student id to be deleted\n");
scanf("%d", &id);
getchar();
// 删除成绩信息
sprintf(query, "delete from score where student_id=%d", id);
if (mysql_real_query(con, query, (unsigned int)strlen(query)) != 0) {
mysql_rollback(con);
printf("delete score failed\n");
}
// 删除学生信息
sprintf(query, "delete from stuinfo where id=%d", id);
if (mysql_real_query(con, query, (unsigned int)strlen(query)) != 0) {
mysql_rollback(con);
printf("delete student failed\n");
}
mysql_commit(con);
if (mysql_affected_rows(con) > 0) {
printf("delete student and score successful\n");
}
displayTable(con, "select * from stuinfo");
displayTable(con, "select * from score");
}
int menu() {
int choose;
printf("*********student information system*********\n");
printf("1. create database\n");
printf("2. create table\n");
printf("3. insert student data\n");
printf("4. delete student data\n");
printf("5. insert score data\n");
printf("6. display score table\n");
printf("7. update score data\n");
printf("8. delete score data\n");
printf("9. delete student and score data\n");
printf("0. exit\n");
printf("please input choose\n");
scanf("%d", &choose);
getchar();
return choose;
}
void handleMenu(MYSQL *mysql) {
int choose;
while (1) {
choose = menu();
switch (choose) {
case 1: createDB(mysql, "Create database student"); break;
case 2:
createTable(mysql, "student", "create table stuinfo(id INT PRIMARY KEY, stuname TEXT, gender TEXT, mailbox TEXT, studep TEXT)");
createTable(mysql, "student", "create table score(student_id INT, course TEXT, grade FLOAT, PRIMARY KEY(student_id, course), FOREIGN KEY(student_id) REFERENCES stuinfo(id) ON DELETE CASCADE)");
break;
case 3: insertData(mysql); break;
case 4: deleteData(mysql); break;
case 5: insertScore(mysql); break;
case 6: displayScoreTable(mysql); break;
case 7: updateScore(mysql); break;
case 8: deleteScore(mysql); break;
case 9: deleteStudentAndScore(mysql); break;
case 0: printf("exit\n"); return; break;
}
}
}
int main(void) {
MYSQL *mysql_handle = NULL;
mysql_handle = initial(mysql_handle);
if (NULL == mysql_handle) {
fprintf(stderr, "initialize failed");
exit(1);
}
mysql_handle = myconnect(mysql_handle, "root", "123456", "localhost", "");
handleMenu(mysql_handle);
mysql_close(mysql_handle);
mysql_library_end();
return 0;
}
实践截图
六、学习实践过程遇到的问题与解决方式
1、MySQL API 使用失败原因及解决方法
-
MySQL API 使用失败(MySQL API Failure) :是指在使用 Linux 系统下的 MySQL API 进行数据库操作时,由于各种因素导致无法正常执行相关操作。常见的失败情况包括数据库连接失败、SQL 查询执行错误、数据插入或更新失败等,这将影响基于 MySQL 数据库的应用程序正常运行,导致数据无法正确存储、读取或更新。
-
失败的原因 :
- 连接配置错误 :错误的数据库主机地址、用户名、密码或端口号等连接配置信息,导致无法与 MySQL 数据库建立连接。例如,使用了错误的主机名或 IP 地址,或者用户名和密码拼写错误。
- 数据库服务问题 :MySQL 数据库服务未正常启动,或者数据库服务器出现故障、过载等情况,使得 API 无法连接到数据库。比如,数据库服务因系统故障意外停止,或者在高并发访问时响应缓慢甚至拒绝连接。
- 权限不足 :使用的数据库用户账号缺乏对相应数据库或表的访问、操作权限,导致执行某些 SQL 操作时失败。例如,尝试向一个表中插入数据,但用户账号没有该表的插入权限。
- SQL 语句错误 :编写的 SQL 语句存在语法错误、拼写错误或者逻辑错误,使得 MySQL 无法正确理解和执行该语句。比如,SQL 关键字拼写错误,或者表名、列名错误等。
- 客户端与服务器版本不兼容 :使用的 MySQL API 客户端版本与服务器版本存在不兼容的情况,可能导致连接失败或某些功能无法正常使用。
- 网络问题 :如果数据库服务器与应用程序服务器不在同一台机器上,网络连接故障、网络延迟过高或网络带宽不足等问题可能会影响 MySQL API 的正常使用,导致连接超时或数据传输错误。
-
解决方法 :
- 检查连接配置 :仔细核对 MySQL 连接配置信息,包括主机地址、端口号、用户名和密码等,确保其正确无误。可以通过在命令行中使用
mysql -h host -u user -p
命令尝试连接数据库,来验证连接配置是否正确。 - 确认数据库服务状态 :在数据库服务器上,使用命令如
systemctl status mysql
(对于 systemd 管理的服务)或查看 MySQL 服务的相关日志文件,确认数据库服务是否正常运行。如果服务未启动,及时启动服务;如果出现故障,根据错误日志进行相应的修复操作。 - 检查并授予权限 :登录到 MySQL 数据库,使用
SHOW GRANTS FOR user
语句查看用户权限,根据需要使用GRANT
语句为用户授予相应的权限。例如,GRANT INSERT, SELECT, UPDATE ON数据库名.表名 TO '用户名'@'主机名'
。 - 排查 SQL 语句错误 :使用 MySQL 客户端工具或其他 SQL 编辑器,对有问题的 SQL 语句进行语法检查和调试,确保其正确性。也可以查看 MySQL 的错误日志,其中可能包含 SQL 语句执行错误的详细信息,帮助定位问题所在。
- 检查版本兼容性 :查看 MySQL 客户端和服务器的版本信息,确保它们之间的兼容性。如果存在版本不兼容问题,考虑升级客户端或服务器端的版本,或者调整 API 使用方式以适应当前的版本。
- 排查网络问题 :使用网络诊断工具如
ping
、traceroute
等检查网络连通性,查看网络延迟和丢包情况。如果网络存在问题,及时与网络管理员沟通解决,或者优化网络配置以确保数据库连接的稳定性和数据传输的可靠性。
- 检查连接配置 :仔细核对 MySQL 连接配置信息,包括主机地址、端口号、用户名和密码等,确保其正确无误。可以通过在命令行中使用
-
应用场景 :在 Linux 系统环境下开发基于 MySQL 数据库的应用程序时,MySQL API 使用失败是较为常见的问题。掌握其失败原因和解决方法,有助于开发人员快速定位和修复数据库相关的问题,保障应用程序的正常运行和数据的准确处理,提高系统的可用性和稳定性。
2、关于Linux Shell编程
问题描述
不清楚 Linux Shell 编程中错误发生的原因和解决方法。
解决方式
-
Linux Shell 编程错误(Shell Script Failure) :是指在编写与执行 Linux Shell 脚本过程中,由于种种原因导致脚本无法按预期正常运行。这可能表现为命令未找到、权限不足、语法错误、变量未定义等,会阻碍脚本任务的完成以及后续相关流程的推进。
-
错误的原因 :
- 命令错误 :输入了错误的命令名称,或者命令的选项、参数使用不当。例如,将 “cp” 命令写成 “cpoy”,或者在 “ls” 命令后错误地使用了不支持的选项。
- 权限问题 :脚本文件本身没有执行权限,或者要操作的文件、目录权限不足,导致无法读取、写入或执行相应操作。
- 语法错误 :Shell 脚本中的语法不符合规范,像缺少 “fi” 导致的 if 语句不完整、循环结构书写错误、变量引用未用正确符号等。
- 变量问题 :变量未定义就使用,或者变量的赋值、引用存在错误,例如忘记用等号连接变量名和值、未将变量值用双引号引起来导致在特定情况下值被错误解析等。
- 环境差异 :不同的 Linux 发行版或运行环境可能导致一些命令的行为不同,或者所需的依赖工具未安装,从而引发脚本错误。
-
解决方法 :
- 检查命令 :仔细核对脚本中的每个命令拼写,查看命令的选项和参数是否正确,可以通过在终端中单独运行命令并查看帮助信息(如 “man 命令名”)来确认。
- 设置权限 :使用 “chmod” 命令为脚本文件添加执行权限,如 “chmod +x script.sh”。对于文件和目录权限问题,根据需要合理使用 “chmod” 和 “chown” 等命令进行权限调整。
- 检查语法 :利用 Shell 的检查功能,如在脚本中加入 “set -n” 选项,可以在不执行脚本的情况下检查语法错误。也可以使用一些在线的 Shell 语法检查工具来辅助发现语法问题。
- 变量处理 :在使用变量前确保已正确定义和赋值,引用变量时养成用双引号的好习惯,如 “echo “$variable””,避免变量值为空或包含特殊字符时出现意外情况。
- 环境适配 :明确脚本所依赖的 Linux 发行版环境以及所需的工具,提前在目标运行环境中安装好这些依赖工具,并在脚本开头注明适用的 Shell 解释器版本等信息,以提高脚本的兼容性。
-
应用场景 :Linux Shell 编程错误在自动化任务处理、系统管理脚本编写以及软件部署等众多场景下都容易出现。了解这些错误原因和解决方法,有助于开发者快速定位问题并修复脚本,保障脚本在不同环境下稳定、高效地执行任务,提高工作效率和系统运维的便捷性。
七、参考资料
- AI工具(你使用的AI工具及其链接)
- 问小白
- Kimi
- 图书
- 《Linux编程基础.李养群》电子版
- 网站
- CSDN
“20242817李臻 原创作品转载请注明出处 《Linux高级编程实践》”