C语言手写迷你DBMS:建表改表与表结构持久化实战
简介这份资源是山东科技大学信息科学与工程学院《数据库系统概论》课程设计的实验报告面向计算机科学与技术专业学生及需要完成类似DBMS实践任务的学习者。报告围绕“实现一个简单的DBMS——创建表和修改表”展开完整记录了从需求分析、设计思想到程序实现的全过程涵盖CREATE TABLE与ALTER TABLE语句的解析、表物理存储结构设计、命令行与图形化界面两种交互方式以及“新建表”“修改表”“显示已建表信息”“保存并退出”四个核心模块的实现思路。资源包为1个doc文档约408KB内容包含课程设计任务书、设计要求、需求分析、设计思想、程序流程图及关键代码说明并列出王珊《数据库系统概论》、谭浩强《C程序设计》等参考资料。目前已有931人学习下载适合作为数据库课程设计参考、实验报告撰写范例及DBMS入门实践的学习材料。1. 从一份课程设计拆起用 C 语言手搓一个能建表改表的迷你 DBMS很多人第一次接触数据库都是从写CREATE TABLE和ALTER TABLE开始的但真正让你理解“表在磁盘上到底长什么样”的往往不是背语法而是自己动手写一个能解析这两条语句的小系统。这份课程设计资源核心就是用 C 语言实现一个极简 DBMS支持创建表、修改表、读取已建表、保存表结构四个功能交互上要求命令行和图形化界面两种形式。它适合正在做数据库课程设计的学生也适合想搞明白 SQL 解析和表结构持久化到底怎么落地的开发者。我拆完这套源码后最大的感受是麻雀虽小但词法扫描、数组存储、文件读写、约束校验这几个环节一个都没少踩坑点也一个都没少。2. 表结构怎么存数组建模与 table.txt 持久化方案2.1 为什么用四维字符数组而不是结构体这份设计最反直觉的地方是它没有用struct来定义表、字段、约束而是用了一组多维字符数组来承载所有元数据。核心定义大致是这样#define LEN 20 #define MAX 50 char tableName[LEN][MAX]; // 表名第一维是表序号 char fieldName[LEN][LEN][MAX]; // 字段名第二维是字段序号 char fieldType[LEN][LEN][MAX]; // 字段类型存 int char 等 char fieldCondition[LEN][LEN][MAX]; // 列级约束存 primary key 等 char f_t_len[LEN][LEN][MAX]; // 字段长度如 (20) int Pro_Num[LEN]; // 每张表的字段个数 int number 0; // 当前已建表数量逻辑说明tableName[0]是第一张表的名字fieldName[0][2]是第一张表第三个字段的名字fieldType[0][2]是它的类型。这种“平铺”写法在课程设计里很常见好处是不用动态内存分配坏处是维度一多就容易写错下标。参数上LEN控制最多建多少张表、每张表最多多少字段MAX控制单个名字或类型字符串的最大长度。我一般会把LEN设成 20、MAX设成 50太小了演示时容易溢出太大了栈上数组会吃紧。2.2 保存表信息先清空再重写的“后悔药”策略持久化这块设计者用了一个很务实的做法每次保存时先把table.txt整体清空再把内存里所有表的信息按固定顺序重新写一遍。这样就不需要处理“修改某张表后原位置对不上”的问题。void Save() { ofstream outFile(table.txt, ios::trunc); // trunc 表示清空重写 outFile number endl; // 先写表的总数 for (int i 0; i number; i) { outFile tableName[i] Pro_Num[i] endl; for (int j 0; j Pro_Num[i]; j) { outFile fieldName[i][j] fieldType[i][j] f_t_len[i][j] fieldCondition[i][j] endl; } } outFile.close(); }逻辑说明第一行写表数量后续每张表先写表名和字段数再逐字段写“字段名 类型 长度 约束”。参数上ios::trunc是关键少了它就会变成追加读回来时表数量对不上直接翻车。读取时按同样顺序反向解析即可用while(!ioFile.eof())配合number逐表恢复。这里有个血泪经验字段名和约束里不能有空格否则按空格分隔的读取逻辑会直接错位所以输入校验阶段必须把空格挡掉。3. CREATE TABLE 解析从字符串到字段数组的完整链路3.1 词法扫描的三个阶段解析CREATE TABLE语句本质是把一整行字符串切成有意义的 token。这份代码的做法是分三段推进先跳过前导空格读第一个词判断是不是create再跳空格读第二个词判断是不是table然后进入字段循环逐个提取字段名、类型、长度、约束。int SQL_CREATE() { int i 0, j 0, num 0; char temp[MAX]; // 阶段一跳过空格读 create for (; SQL[i] ; i); for (j 0; SQL[i] ! SQL[i] ! ;; i, j) temp[j] tolower(SQL[i]); temp[j] \0; if (strcmp(temp, create) ! 0) { printf(你输入的 create 有误请重新创建!\n); return 0; } // 阶段二跳空格读 table for (; SQL[i] ; i); for (j 0; SQL[i] ! SQL[i] ! ;; i, j) temp[j] tolower(SQL[i]); temp[j] \0; if (strcmp(temp, table) ! 0) { printf(你输入的 table 有误请重新创建!\n); return 0; } // 阶段三提取表名进入字段循环 // ... }逻辑说明tolower保证大小写不敏感SQL[i] ! SQL[i] ! ;是 token 的边界条件。参数上i是全局扫描指针j是当前 token 的写入位置。常见做法是每读完一个 token 就补\0否则strcmp会读到上一次的残留内容这种玄学 bug 在 VC6 里尤其难查。3.2 字段类型与长度的提取规则字段类型只认五种char、int、float、double、date。代码里用两个平行数组做映射一个存类型字符串一个存单字符编码char type[5][LEN] {char, int, float, double, date}; char Type1[5] {C, I, F, B, D};逻辑说明匹配到类型后只有char、float、double需要读长度int和date直接跳过。长度提取时逐字符判断是不是数字遇到非数字就报错if (f_Type C || f_Type F || f_Type B) { for (; SQL[i] ; i); for (; SQL[i] ! ; i) { if (SQL[i] 0 || SQL[i] 9) { flag 0; break; } f_length f_length * 10 (SQL[i] - 0); } if (flag 0) { printf(你输入的字段长度有误请重新创建!\n); return 0; } // 拼成 (20) 形式存入 f_t_len }参数说明f_length是累乘出来的整型长度值最后要转回字符串存进f_t_len。这里有个隐藏坑——原代码用f_length 48把长度转成字符只能处理 0 到 9 的长度一旦写char(20)就会存成乱码。我一般会改成sprintf或itoa来拼字符串这是复现时第一个要修的地方。3.3 约束条件的识别与存储约束支持三种primary key、unique、not null。识别逻辑是读一个词如果是primary就再读一个词确认是key如果是not就再读一个词确认是null如果是unique直接存。if (strcmp(condition, primary) 0) { for (; SQL[i] ; i); for (j 0; SQL[i] ! SQL[i] ! ,; i, j) temp[j] tolower(SQL[i]); temp[j] \0; if (strcmp(temp, key) 0) strcpy(fieldCondition[number][num], primary key); else { printf(你输入的 KEY 有误请重新创建!\n); return 0; } } else if (strcmp(condition, unique) 0) { strcpy(fieldCondition[number][num], unique); } else if (strcmp(condition, not) 0) { // 继续读 null }逻辑说明约束是可选字段如果当前 token 是逗号或分号说明这个字段没有约束直接存NULL。参数上condition存当前读到的约束词temp存第二个词。注意primary key中间的空格必须处理掉否则存进文件后读取时会多出一个 token导致后续字段全部错位。4. ALTER TABLE 解析ADD、DROP、MODIFY 三条分支怎么走4.1 先查表是否存在再分派子命令ALTER TABLE的第一步不是解析动作而是确认表名存在。代码用一个循环在tableName数组里查找找到就记下下标al_num找不到直接报错返回。int al_num; for (al_num 0; al_num number; al_num) { if (strcmp(tableName[al_num], alterName) 0) break; } if (al_num number) { printf(你输入的表 %s 不存在请重新输入 SQL 语句!\n, alterName); return 0; }逻辑说明number是当前表总数循环走完还没break就说明没找到。参数上alterName是从语句里提取的表名al_num是后续修改的目标表下标。这一步是很多同学容易漏的——不查存在性就直接改数组越界后程序行为完全不可预测。4.2 ADD 分支追加字段并更新字段计数ADD 分支的逻辑和 CREATE 里的字段提取几乎一样区别是它把新字段追加到Pro_Num[al_num]的位置然后把字段计数加一。if (tolower(SQL[i]) a) { // 确认是 add // 提取字段名 for (; SQL[i] ; i); for (j 0; SQL[i] ! ; i, j) field_name[j] SQL[i]; field_name[j] \0; // 提取类型、长度、约束存入 fieldName[al_num][Pro_Num[al_num]] 等 // ... Pro_Num[al_num]; // 字段数加一 }逻辑说明新字段的下标就是当前Pro_Num[al_num]存完再自增。参数上field_name是临时缓冲最终要拷进三维数组的对应位置。常见做法是复用 CREATE 里的类型解析函数避免两处逻辑不一致。4.3 DROP 与 MODIFY 分支删除和改类型的边界DROP 分支按约束名删除列级约束MODIFY 分支修改字段类型。这两个分支的代码在原设计里相对简略复现时需要补全。DROP 的核心是找到约束名对应的字段把fieldCondition置空MODIFY 的核心是找到字段名覆盖fieldType和f_t_len。// DROP 分支示意 for (int k 0; k Pro_Num[al_num]; k) { if (strcmp(fieldCondition[al_num][k], dropName) 0) { strcpy(fieldCondition[al_num][k], NULL); break; } } // MODIFY 分支示意 for (int k 0; k Pro_Num[al_num]; k) { if (strcmp(fieldName[al_num][k], modifyName) 0) { strcpy(fieldType[al_num][k], newType); // 同步更新 f_t_len break; } }逻辑说明DROP 删的是约束不是字段这点和标准 SQL 有出入复现时要看清题目要求。MODIFY 改类型后必须同步改长度否则保存到文件再读回来时类型和长度对不上。参数上dropName和modifyName都是从语句里提取的目标名。5. 避坑与排查这套课程设计里最容易翻车的五个点5.1 现象保存后重启程序表数量变成 0原因Save()里用了ios::trunc清空文件但Read()里第一行读表数量的逻辑和写入顺序不一致或者文件路径写死成了某个绝对路径换机器后读不到。解决统一用相对路径table.txt读写顺序严格对齐——先读表数量再循环读每张表的表名、字段数、字段详情。每次改完保存逻辑手动打开table.txt核对一遍格式。5.2 现象输入char(20)后长度显示成乱码原因原代码用f_length 48把整型转字符只能处理 0 到 9。长度超过 9 时高位丢失。解决改用sprintf(f_t_len[number][num], (%d), f_length)或者手写一个整数转字符串的小函数。这是复现时必改的一处。5.3 现象primary key存进去后读回来变成两个字段原因保存时把primary key当成一个字符串写进文件中间有空格读取时按空格分隔primary和key被拆成两个 token后续字段全部错位。解决要么保存时把空格换成下划线如primary_key读取时再换回来要么读取时用getline按行读自己解析。我一般选前者改动最小。5.4 现象ALTER 修改后显示的还是旧结构原因修改的是内存数组但没有触发保存或者保存时number没更新导致新字段没写进文件。解决每次 ALTER 成功后立即调用一次Save()并在Save()开头重新计算number。调试时可以在保存后立刻调用Read()回读一遍验证一致性。5.5 现象VC6 编译通过CodeBlocks 里报错原因VC6 对 C 标准宽松fstream的open模式、strcpy的安全性检查在不同编译器下行为不同。原代码里ioFile.open(C:\\Users\\Administrator\\Desktop\\DBMS.txt, ios::in)这种绝对路径换机器直接失效。解决统一改成ioFile.open(table.txt, ios::in)头文件补上fstream和cstring避免用gets等已废弃函数。如果必须在两个 IDE 间切换把文件路径做成宏或命令行参数。6. 进阶技巧把解析器改成表驱动再加一个回读校验这套课程设计跑通之后最值得做的一件事是把散落在SQL_CREATE和SQL_ALTER里的解析逻辑抽成表驱动。具体做法是定义一个关键字表把create、table、add、drop、modify、primary、unique、not、null这些词和对应的处理函数指针放进去主循环只负责读 token、查表、调用。typedef struct { const char *keyword; int (*handler)(int *pos); } KeywordEntry; KeywordEntry keywords[] { {create, handle_create}, {table, handle_table}, {add, handle_add}, {drop, handle_drop}, {modify, handle_modify}, {NULL, NULL} };逻辑说明每个handler接收当前扫描位置返回下一个位置或错误码。这样加新关键字只需要加一行表项不用改主流程。参数上pos是指向SQL下标的指针方便 handler 内部推进。另一个实用技巧是加一个回读校验函数每次Save()之后立刻Read()到另一组临时数组逐字段比对内存和文件是否一致。不一致就打印差异位置直接定位是哪个字段写错了。int VerifyAfterSave() { // 备份当前内存状态 // 清空内存调用 Read() // 逐表逐字段比对 tableName、fieldName、fieldType、fieldCondition // 返回不一致的字段数量 }参数说明这个函数只在调试阶段开启正式演示时关掉避免每次保存都多一次磁盘 IO。我一般会在main里加一个#ifdef DEBUG开关来控制。从那以后我每次做完这种带持久化的课程设计都强制走一遍“保存—回读—比对”的流程宁可多花十分钟也不想到答辩时被问“你改完的表存哪了”答不上来。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Windows+WSL+Anaconda+Cursor断点调试完整配置指南

Windows+WSL+Anaconda+Cursor断点调试完整配置指南

刚把项目环境从 Windows 搬到 WSL 下的 Linux 时,我遇到的最大麻烦并不是跑不起来,而是断点调试静悄悄。代码能执行,print 能打印,但一按调试按钮,断点全部灰色,笔记本风扇转得飞起,调试控制台里…

2026/10/13 12:54:06 阅读更多 →
AI Agent 驱动的自然语言操作:让业务系统听懂人话的开源实现

AI Agent 驱动的自然语言操作:让业务系统听懂人话的开源实现

> 系列:《VeapAI 自然语言操作实战》,本篇为系列总览。> 文中涉及的表名、字段名、枚举值与接口路径均已对照仓库 veap-cloud/DB/veap.sql 与源码核对;代码路径可直接在仓库中定位。## 一、起因:一次「查个数据要提单」上个…

2026/10/13 12:54:06 阅读更多 →
别再手写 curl 调飞书接口了:这个官方 CLI 让人和 AI 都能用一条命令干活

别再手写 curl 调飞书接口了:这个官方 CLI 让人和 AI 都能用一条命令干活

从一个小需求说起上礼拜我想搞个自动化的东西:每天早上把我在飞书里的当天日程拉下来,整理成一句话扔进群里。放到以前,这条路是这样的:先去飞书开放平台建个自建应用(就是给公司/个人在飞书后台注册一个"程序身份…

2026/10/13 12:54:06 阅读更多 →

最新新闻

DRF接口设计实战:十大接口标准化写法与视图家族选型指南

DRF接口设计实战:十大接口标准化写法与视图家族选型指南

1. 从“能跑通”到“敢上线”:DRF接口设计的真实分水岭刚接触Django REST Framework(后面统一叫DRF)的人,几乎都会经历同一个阶段:照着文档把序列化器、视图、路由拼起来,接口能返回JSON,就觉得…

2026/10/13 13:33:14 阅读更多 →
神经网络计算过程:从前向传播到硬件执行的全链路拆解

神经网络计算过程:从前向传播到硬件执行的全链路拆解

1. 这不是黑箱,是可拆解的流水线:从“神经网络的计算过程”说起很多人第一次听说“神经网络”,脑子里立刻浮现出一堆密密麻麻的节点和箭头,再配上“深度学习”“反向传播”这类词,下意识就觉得——这东西得是数学博士才…

2026/10/13 13:33:14 阅读更多 →
麻将图像识别实战:细粒度分类+空间定位+截屏处理闭环

麻将图像识别实战:细粒度分类+空间定位+截屏处理闭环

简介:本资源是一套基于VC2010实现的麻将图像识别实战项目,面向计算机视觉初学者与C图像处理开发者,解决屏幕截取麻将牌面后自动识别牌型(如筒、条、万、字牌)的核心问题,适用于智能麻将桌辅助系统、游戏AI开…

2026/10/13 13:33:14 阅读更多 →
2026国产协作工具实测:高性价比选型指南

2026国产协作工具实测:高性价比选型指南

1. 先聊清楚:2026年为什么还要专门测一轮国产协作工具 1.1 国产协作软件这几年的变化 这两年聊到团队协作工具,圈子里绕不开的名字已经从早期的"外资三件套"变成了PingCode、飞项、板栗看板这类国产选手。早个五六年,大家选协作工…

2026/10/13 13:33:14 阅读更多 →
Kubernetes弹性伸缩实战:HPA、VPA与集群自动伸缩全解析

Kubernetes弹性伸缩实战:HPA、VPA与集群自动伸缩全解析

Kubernetes弹性伸缩这套东西,我前前后后折腾了快三年。最开始以为它就是给Deployment配个HPA,CPU到了80%就多开几个Pod,后来才发现这套体系的深度远不止于此。如果你也在维护K8s集群,或者正准备给业务加上弹性伸缩能力&#xff0c…

2026/10/13 13:33:14 阅读更多 →
电赛捡球小车鲁棒性设计:STM32+OpenMV嵌入式运动控制实战

电赛捡球小车鲁棒性设计:STM32+OpenMV嵌入式运动控制实战

简介:本资源为2024年全国大学生电子设计竞赛‘捡球小车’赛题的完整工程实现与配套资料,面向电赛备赛学生、嵌入式初学者及智能车实践爱好者,聚焦自动循迹、球体识别与机械抓取等核心功能的软硬件协同实现。压缩包共344个文件,涵盖…

2026/10/13 13:32:12 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →