在嵌入式Linux开发中,高效、轻量地管理数据是常见需求。SQLite作为一款开源、嵌入式的关系型数据库,无需独立的服务器进程,整个数据库就是一个文件,完美契合嵌入式场景。本文将带你从零开始,全面掌握SQLite在C语言环境下的使用。
SQLite简介与安装
SQLite是一个遵守ACID(原子性、一致性、隔离性、持久性)的关系数据库管理系统,它包含在一个相对小的C语言库中。与许多其他数据库管理系统不同,SQLite不是一个客户端/服务器结构的数据库引擎,而是被集成在用户程序中。
在Ubuntu等基于Debian的Linux系统上,安装SQLite非常简单。首先确保你的虚拟机网络连通且apt-get源已正确配置,然后执行以下命令:
cpp
# 安装SQLite3命令行工具、开发库和图形化浏览器
sudo apt-get install sqlite3
sudo apt-get install libsqlite3-dev
sudo apt-get install sqlitebrowser
安装完成后,在终端输入sqlite3即可进入交互式命令行环境,输入.quit退出。
SQLite基础操作与SQL语法
常用SQLite命令
在sqlite3命令行环境中,点号(.)开头的命令用于管理数据库本身:
-
.databases:显示当前打开的数据库文件。 -
.tables:列出当前数据库中的所有表。 -
.schema 表名:显示创建指定表的SQL语句。 -
.header on/.header off:开启/关闭查询结果的列名显示。 -
.mode 格式:设置结果输出格式,如column(列模式)、list(列表模式)、csv等。 -
.quit:退出sqlite3。
核心SQL语句
SQL(Structured Query Language)是操作数据库的标准语言。以下是SQLite中最常用的SQL语句:
-
创建表 (CREATE TABLE)
定义表的结构,包括列名和数据类型。
cpp-- 创建一个学生表,包含学号、姓名、性别、年龄、成绩字段 -- INTEGER PRIMARY KEY 表示学号是整数类型的主键(唯一且自增) -- TEXT 表示文本类型 CREATE TABLE student ( 学号 INTEGER PRIMARY KEY, 姓名 TEXT, 性别 TEXT, 年龄 INTEGER, 成绩 INTEGER ); -
删除表 (DROP TABLE)
永久删除一张表及其所有数据。
DROP TABLE student; -
插入数据 (INSERT INTO)
向表中添加新的数据行。
cpp-- 为所有列插入值,值的顺序必须与表定义一致 INSERT INTO student VALUES (1001, '张三', '男', 15, 90); -- 为指定列插入值 INSERT INTO student (学号, 姓名, 性别) VALUES (1002, '李四', '女'); -
删除数据 (DELETE FROM)
根据条件删除表中的数据行。
cpp-- 删除姓名为'王五'的学生记录 DELETE FROM student WHERE 姓名='王五'; -- 删除成绩小于60分的学生记录 DELETE FROM student WHERE 成绩 < 60; -
更新数据 (UPDATE)
修改表中已存在的数据。
cpp-- 将张三的年龄更新为16岁,成绩更新为95分 UPDATE student SET 年龄=16, 成绩=95 WHERE 姓名='张三'; -
查询数据 (SELECT)
从表中检索数据,这是最常用、最灵活的语句。
cpp-- 查询student表中的所有列和所有行 SELECT * FROM student; -- 只查询学号和成绩两列 SELECT 学号, 成绩 FROM student; -- 查询成绩大于80分的学生姓名 SELECT 姓名 FROM student WHERE 成绩 > 80; -- 查询所有姓'张'的学生(使用LIKE和通配符%) SELECT * FROM student WHERE 姓名 LIKE '张%'; -- 查询结果按成绩降序排列(DESC: 降序, ASC: 升序) SELECT * FROM student ORDER BY 成绩 DESC; -
多表联合查询
当信息分散在多个表中时,需要联合查询。
- 内连接 (INNER JOIN):返回两个表中匹配成功的记录。
cpp-- 假设有'学生信息'和'学生成绩'表,通过'学号'关联 SELECT 学生信息.姓名, 学生成绩.成绩 FROM 学生信息 INNER JOIN 学生成绩 ON 学生信息.学号 = 学生成绩.学号;-
左外连接 (LEFT OUTER JOIN):返回左表所有记录,以及右表中匹配的记录,无匹配则为NULL。
SELECT 学生信息.姓名, 学生成绩.成绩
FROM 学生信息
LEFT OUTER JOIN 学生成绩 ON 学生信息.学号 = 学生成绩.学号;
C语言编程操作SQLite数据库
要在C程序中使用SQLite,需要包含头文件#include <sqlite3.h>,并在编译时链接-lsqlite3库。
核心API函数
-
打开数据库:
sqlite3_open此函数用于打开(或创建)一个数据库文件,并获得一个用于后续操作的数据库句柄。
cppint sqlite3_open( const char *filename, // 数据库文件路径 (UTF-8编码) sqlite3 **ppDb // 输出参数:数据库句柄指针的指针 ); // 成功返回 SQLITE_OK (0),失败返回错误代码。 -
执行SQL语句:
sqlite3_exec这是一个万能函数,用于执行不返回数据的SQL语句(如CREATE, INSERT, UPDATE, DELETE),或执行SELECT并提供一个回调函数处理结果。
cppint sqlite3_exec( sqlite3* db, // 数据库句柄 const char *sql, // 要执行的SQL语句字符串 int (*callback)(void*,int,char**,char**), // 回调函数(SELECT时用) void *arg, // 传递给回调函数的参数 char **errmsg // 错误信息指针 ); // 成功返回 SQLITE_OK。 -
错误信息:
sqlite3_errmsg当API调用失败时,通过此函数获取可读的错误描述。
cppconst char *sqlite3_errmsg(sqlite3* db); -
关闭数据库:
sqlite3_close关闭数据库连接,释放资源。
int sqlite3_close(sqlite3* db); -
释放内存:
sqlite3_free释放由SQLite库函数(如
sqlite3_exec的errmsg)分配的内存。void sqlite3_free(void* ptr);
实战示例:单词查询程序
下面我们实现一个简单的单词查询程序,模拟文档中提到的作业。程序打开一个包含单词表的数据库,并允许用户查询单词含义。
cpp
#include <sqlite3.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
typedef struct dict
{
char name[32];
char text[256];
}dict_t;
int pcallbackfun(void *parg, int columno, char **pcontents, char **pheaders)
{
int i = 0;
printf("=========================\n");
for (i = 0; i < columno; i++)
{
printf("%s:%s\n", pheaders[i], pcontents[i]);
}
printf("=========================\n");
return 0;
}
int main(void)
{
int ret = 0;
int i = 0;
int found = 0;
char *ptmp = 0;
char word[32] = {0};
char tmpbuff[256] = {0};
char cmdbuf[1024] = {0};
char *perrmsg = NULL;
ssize_t nret = 0;
FILE *fd = 0;
dict_t wordlist = {0};
sqlite3 *pdb = 0;
//open
fd = fopen("dict.txt", "r");
if (fd == NULL)
{
perror("fail to open");
return -1;
}
//sqlite_open
ret = sqlite3_open("dict.db",&pdb);
if(ret != SQLITE_OK)
{
perror("fail to sqlite3_open");
return -1;
}
sprintf(cmdbuf, "create table if not exists dict (name text,text1 text);");
ret = sqlite3_exec(pdb, cmdbuf, NULL, NULL, &perrmsg);
if (ret != SQLITE_OK)
{
fprintf(stderr, "fail to sqlite3_exec:%s\n", perrmsg);
sqlite3_free(perrmsg); //释放空间
sqlite3_close(pdb);
return -1;
}
//read
//sqlite_exec
while(1)
{
char *pname = 0;
char *ptext = 0;
memset(wordlist.name,0,sizeof(wordlist.name));
memset(wordlist.text,0,sizeof(wordlist.text));
ptmp = fgets(tmpbuff, sizeof(tmpbuff), fd);
if(NULL == ptmp)
{
break;
}
pname = tmpbuff;
while(*pname != ' ')
{
pname++;
}
*pname = '\0';
ptext = pname + 1;
while(*ptext == ' ')
{
ptext++;
}
strncpy(wordlist.name, tmpbuff, sizeof(wordlist.name)-1);
wordlist.name[sizeof(wordlist.name)-1] = '\0'; // 确保终止
strcpy(wordlist.text,ptext);
sprintf(cmdbuf, "insert into dict values(\"%s\", \"%s\");", wordlist.name, wordlist.text);
ret = sqlite3_exec(pdb, cmdbuf, NULL, NULL, &perrmsg);
if (ret != SQLITE_OK)
{
fprintf(stderr, "fail to sqlite3_exec:%s\n", perrmsg);
sqlite3_free(perrmsg);
sqlite3_close(pdb);
return -1;
}
}
printf("lll\n");
gets(word);
sprintf(cmdbuf, "select name,text1 from dict where name = '%s';", word);
ret = sqlite3_exec(pdb, cmdbuf, pcallbackfun, NULL, &perrmsg);
if (ret != SQLITE_OK)
{
fprintf(stderr, "fail to sqlite3_exec:%s\n", perrmsg);
sqlite3_free(perrmsg);
sqlite3_close(pdb);
return -1;
}
//sqlite_close
//close
sqlite3_close(pdb);
fclose(fd);
return 0;
}
总结
SQLite以其零配置、无服务器、单文件的特性,成为嵌入式系统、移动应用和小型桌面应用的理想数据库选择。通过掌握基本的SQL语法和C语言API,你可以在你的嵌入式Linux项目中轻松实现数据持久化功能。记住,在实际开发中,务必使用参数化查询来防范SQL注入攻击,并注意及时关闭数据库连接、释放资源,以确保程序的健壮性和安全性。