停车场管理系统 v1.0–详细讲解,附带源码
项目简介
这是一个功能完善的停车场管理系统,使用C语言开发,采用控制台交互界面。系统支持车辆进出管理、停车费用计算、车位管理、数据查询统计、数据持久化等完整功能。
主要特性
- 车辆进出管理:记录车辆进入和离开,自动分配车位
- 智能费用计算:根据停车时长自动计算费用,支持免费时长设置
- 车位管理:自动分配和释放车位,实时显示空余车位
- 数据查询:按车牌号查询、显示所有记录、统计信息
- 配置管理:可配置总车位、每小时费用、免费时长等参数
- 数据持久化:CSV格式存储,易于查看和编辑
- 数据备份功能:自动备份,支持数据恢复
- 友好的用户界面:彩色输出、清晰的菜单结构
- 详细代码注释:所有函数都有详细的中文注释
运行界面
1. 主界面

2. 停车场管理界面

3. 数据查询界面

4. 系统设置界面

等等界面,内容完善!
快速开始
编译方法
Windows (MinGW)
gcc parking_manager.c -o parking_manager.exe
Linux/Mac
gcc parking_manager.c -o parking_manager
运行程序
Windows
parking_manager.exe
Linux/Mac
./parking_manager
功能详细说明
1. 停车场管理
1.1 车辆进入
- 功能:记录车辆进入停车场
- 输入项:
- 车牌号*(必填,不能重复进入)
- 车辆类型(可选,如:小型车、大型车等)
- 自动处理:
- 自动分配可用车位号
- 记录进入时间(精确到秒)
- 更新空余车位数量
- 验证规则:
- 车牌号不能为空
- 同一车牌号不能重复进入(如果车辆还在停车场内)
- 停车场必须有空余车位
1.2 车辆离开
- 功能:记录车辆离开停车场并计算费用
- 输入项:
- 车牌号(必填)
- 自动处理:
- 记录离开时间
- 自动计算停车费用
- 释放车位
- 更新空余车位数量
- 费用计算规则:
- 免费时长内不收费(默认15分钟)
- 超过免费时长按小时计费(不足一小时按一小时计算)
- 费用 = (停车时长 - 免费时长) / 60 * 每小时费用
1.3 查询停车场状态
- 功能:显示停车场的实时状态信息
- 显示内容:
- 总车位数量
- 已停车位数量
- 空余车位数量
- 使用率(百分比)
- 正在停车的车辆列表(车牌号、车辆类型、车位号、进入时间)
2. 数据查询
2.1 按车牌号查询
- 功能:查询指定车牌号的停车记录
- 显示内容:
- 车牌号
- 车辆类型
- 车位号
- 进入时间
- 离开时间(如果已离开)
- 停车费用(如果已离开)
- 当前状态(正在停车中或已离开)
- 已停车时长(如果正在停车)
2.2 显示所有记录
- 功能:以表格形式显示所有停车记录
- 显示内容:
- 车牌号、车辆类型、车位号
- 进入时间、离开时间
- 停车费用
- 说明:包括已完成和正在进行的停车记录
2.3 统计信息
- 功能:显示停车场的统计信息
- 统计内容:
- 今日统计:
- 今日进入车辆数量
- 今日收入
- 当前状态:
- 正在停车的车辆数量
- 空余车位数量
- 累计统计:
- 总收入
- 总记录数
- 今日统计:
3. 系统设置
3.1 查看配置
- 功能:显示当前停车场配置
- 显示内容:
- 总车位数量
- 每小时费用(元)
- 免费时长(分钟)
3.2 修改配置
- 功能:修改停车场配置参数
- 可修改项:
- 总车位数量(1-1000)
- 每小时费用(元,>=0)
- 免费时长(分钟,>=0)
- 说明:修改后自动保存到配置文件
3.3 保存数据
- 功能:将当前内存中的数据保存到CSV文件
- 默认文件:
parking_records.csv - 说明:建议在退出程序前保存数据
3.4 备份数据
- 功能:创建数据备份文件
- 备份位置:
backups/目录 - 文件命名:
backup_YYYYMMDD_HHMMSS.csv - 说明:
- 备份文件包含时间戳,便于版本管理
- 自动创建备份目录(如果不存在)
3.5 加载数据文件
- 功能:从CSV文件加载停车记录数据
- 操作:可以指定文件名,默认加载
parking_records.csv - 说明:
- 加载的数据会与现有数据合并
- 自动更新空余车位数量
3.6 导出CSV文件
- 功能:将数据导出到指定的CSV文件
- 用途:数据迁移、数据分享、Excel分析
- 格式:标准CSV格式,可用Excel打开
? 数据文件格式
CSV文件结构
系统使用CSV(逗号分隔值)格式存储停车记录,文件结构如下:
车牌号,车辆类型,车位号,进入时间,离开时间,费用
京A12345,小型车,1,2024-12-01 10:30:00,2024-12-01 12:45:00,15.00
京B67890,大型车,2,2024-12-01 11:00:00,未记录,0.00
字段说明
| 字段 | 类型 | 说明 | 必填 |
|---|---|---|---|
| 车牌号 | 字符串 | 车辆牌照号码 | ? |
| 车辆类型 | 字符串 | 车辆类型(如:小型车、大型车) | ? |
| 车位号 | 整数 | 分配的车位号(从1开始) | ? |
| 进入时间 | 时间字符串 | 进入时间,格式:YYYY-MM-DD HH:MM:SS | ? |
| 离开时间 | 时间字符串 | 离开时间,格式同上,未离开显示"未记录" | ? |
| 费用 | 浮点数 | 停车费用(元),未离开为0.00 | ? |
配置文件格式
系统使用文本文件存储配置,文件名为 parking_config.txt:
totalSpots=100
feePerHour=5.0
freeMinutes=15
数据导入注意事项
- 文件编码:建议使用UTF-8编码,避免中文乱码
- 表头:第一行必须是表头,系统会自动识别
- 字段顺序:必须按照上述顺序排列
- 时间格式:时间必须严格按照
YYYY-MM-DD HH:MM:SS格式 - 数据验证:
- 车牌号不能为空
- 车位号必须是正整数
- 费用必须是数字
? 使用示例
示例1:车辆进入停车场
1. 选择"停车场管理" -> "车辆进入"
2. 输入车牌号:京A12345
3. 输入车辆类型:小型车(可选)
4. 系统自动分配车位号(如:1号车位)
5. 系统记录进入时间:2024-12-01 10:30:00
6. 显示进入成功信息
示例2:车辆离开停车场
1. 选择"停车场管理" -> "车辆离开"
2. 输入车牌号:京A12345
3. 系统自动计算:
- 进入时间:2024-12-01 10:30:00
- 离开时间:2024-12-01 12:45:00
- 停车时长:2小时15分钟
- 免费时长:15分钟
- 计费时长:2小时
- 停车费用:2 * 5.0 = 10.00 元
4. 释放车位,更新空余车位数量
示例3:查询停车场状态
1. 选择"停车场管理" -> "查询停车场状态"
2. 系统显示:
- 总车位:100
- 已停车位:25
- 空余车位:75
- 使用率:25.0%
- 正在停车的车辆列表
示例4:修改配置
1. 选择"系统设置" -> "修改配置"
2. 输入总车位数量:150
3. 输入每小时费用:6.0
4. 输入免费时长:20
5. 系统保存配置到文件
技术实现
数据结构
// 停车记录结构体
typedef struct {
char plate[PLATE_LEN]; // 车牌号
char carType[CAR_TYPE_LEN]; // 车辆类型
int spotNumber; // 车位号
time_t enterTime; // 进入时间(时间戳)
time_t exitTime; // 离开时间(时间戳,0表示未离开)
double fee; // 停车费用
int isDeleted; // 删除标记
} ParkingRecord;
// 停车场配置结构体
typedef struct {
int totalSpots; // 总车位数量
int freeSpots; // 当前空余车位
double feePerHour; // 每小时费用(元)
int freeMinutes; // 免费时长(分钟)
} ParkingConfig;
核心算法
-
车位分配算法:
- 使用线性查找,找到第一个空余车位
- 时间复杂度:O(n),n为总车位数
- 车位号从1开始连续分配
-
费用计算算法:
- 计算停车时长(秒转分钟)
- 扣除免费时长
- 按小时向上取整计费
- 公式:费用 = ceil((时长 - 免费时长) / 60) * 每小时费用
-
数据存储:
- 使用静态数组存储,最大容量10000条记录
- CSV格式持久化,便于查看和编辑
- 支持软删除(isDeleted标记)
-
时间处理:
- 使用
time_t类型存储时间戳 - 使用
localtime()转换为本地时间 - 使用
strftime()格式化时间显示
- 使用
平台兼容性
- Windows:完全支持,包含彩色输出和清屏功能
- Linux/Mac:基本功能支持,部分UI功能可能受限
配置说明
可配置参数
在源代码中可以修改以下常量:
#define PLATE_LEN 20 // 车牌号最大长度
#define CAR_TYPE_LEN 20 // 车辆类型最大长度
#define MAX_PARKING_SPOTS 1000 // 最大停车位数量
#define MAX_RECORDS 10000 // 最大记录数量
#define DEFAULT_TOTAL_SPOTS 100 // 默认总车位
#define DEFAULT_FEE_PER_HOUR 5.0 // 默认每小时费用(元)
#define DEFAULT_FREE_MINUTES 15 // 默认免费时长(分钟)
费用计算规则
系统支持以下计费方式(可在代码中修改):
-
按小时计费(当前实现):
- 免费时长内不收费
- 超过免费时长按小时计费
- 不足一小时按一小时计算
-
可扩展的计费方式:
- 可以修改
calculate_fee()函数实现其他计费方式 - 如:按分钟计费、分时段计费、会员折扣等
- 可以修改
注意事项
数据安全
- 定期备份:建议定期使用备份功能保存数据
- 数据验证:系统会对输入数据进行基本验证,但仍需谨慎输入
- 文件权限:确保程序对数据文件有读写权限
- 文件编码:使用UTF-8编码,避免中文乱码
使用建议
- 首次使用:建议先配置停车场参数(总车位、费用等)
- 定期保存:修改数据后及时保存,避免数据丢失
- 备份策略:建议在重要操作前先备份数据
- 时间同步:确保系统时间准确,影响费用计算
已知限制
- 数据规模:最大支持10000条停车记录
- 车位数量:最大支持1000个车位
- 并发访问:不支持多用户同时访问同一数据文件
- 时间精度:时间精度为秒,不支持毫秒级精度
- 计费方式:当前仅支持按小时计费,不支持更复杂的计费规则
常见问题
Q1: 程序无法编译?
A: 检查以下几点:
- 确保已安装C编译器(GCC/MinGW)
- 检查源代码文件是否完整
- 查看编译错误信息,可能是语法错误
Q2: 中文显示乱码?
A:
- Windows:确保控制台支持UTF-8编码
- 可以在代码中添加
system("chcp 65001");设置UTF-8编码
- 可以在代码中添加
- Linux/Mac:设置环境变量
export LANG=zh_CN.UTF-8 - 或者使用支持UTF-8的终端
Q3: 费用计算不正确?
A:
- 检查系统时间是否正确
- 检查配置的每小时费用和免费时长
- 查看费用计算逻辑是否符合需求
- 注意:不足一小时按一小时计算
Q4: 车位分配错误?
A:
- 检查总车位配置是否正确
- 确保车辆离开时正确释放车位
- 查看是否有重复的车位分配
Q5: 数据文件无法打开?
A:
- 检查文件路径是否正确
- 确保文件没有被其他程序占用
- 检查文件权限
- 检查文件编码是否为UTF-8
Q6: 如何修改计费规则?
A:
- 修改
calculate_fee()函数 - 可以改为按分钟计费、分时段计费等
- 修改后重新编译程序
Q7: 可以导入Excel文件吗?
A:
- 可以!将Excel文件另存为CSV格式
- 确保字段顺序与系统要求一致
- 使用"加载数据文件"功能导入
- 注意:Excel导出的CSV可能需要调整编码为UTF-8
版本历史
v1.0 (当前版本)
- 完整的车辆进出管理功能
- 智能费用计算(支持免费时长)
- 自动车位分配和释放
- 停车场状态实时查询
- 数据查询和统计功能
- 配置管理功能
- 数据持久化(CSV格式)
- 自动备份功能
- 数据导入导出功能
- 友好的用户界面(彩色输出、清晰菜单)
- 详细的代码注释
许可证
本项目仅供学习和教育使用。
开发信息
- 开发语言:C语言
- 开发环境:支持Windows/Linux/Mac
- 编译器要求:GCC 或兼容的C编译器
- 标准库:C标准库(stdio.h, stdlib.h, string.h, time.h等)
学习建议
本系统适合用于:
- C语言学习项目
- 数据结构与算法实践
- 文件操作练习
- 控制台程序开发
- 时间处理练习
- 业务逻辑实现
可以改进的方向
-
数据结构优化:
- 使用链表或树结构支持更大数据量
- 使用哈希表提高查找效率
- 使用优先队列优化车位分配
-
数据库集成:
- 使用SQLite替代CSV文件
- 支持更复杂的数据查询
- 支持事务处理
-
图形界面:
- 开发GUI版本(如使用GTK+或Qt)
- 提供更友好的用户体验
- 可视化车位状态
-
网络功能:
- 支持多用户网络访问
- 数据云端同步
- 远程管理功能
-
高级功能:
- 会员管理(会员折扣、积分等)
- 预约停车功能
- 多种计费方式(分时段、包月等)
- 车牌识别(图像识别)
- 自动缴费(移动支付)
-
报表功能:
- 自动生成日报、月报、年报
- 收入统计分析
- 车位利用率分析
-
硬件集成:
- 道闸控制
- 车牌识别摄像头
- LED显示屏
- 语音提示
技术支持
如有问题或建议,请:
- 检查本文档的常见问题部分
- 查看源代码注释
- 检查数据文件格式是否正确
- 查看编译错误信息
使用技巧
-
合理配置参数:
- 根据实际停车场情况设置总车位数
- 根据市场行情设置合理的收费标准
- 设置合适的免费时长(通常15-30分钟)
-
定期备份数据:
- 建议每天备份一次数据
- 重要操作前先备份
- 保留多个历史备份
-
数据整理:
- 定期清理过期的停车记录
- 保持数据的准确性和完整性
- 定期检查车位分配是否正确
-
统计分析:
- 定期查看统计信息
- 分析车位利用率
- 优化停车场管理策略
-
导出分析:
- 可以将数据导出为CSV文件
- 在Excel中进行分析
- 生成图表和报表
代码结构说明
主要模块
-
工具函数模块:
- 颜色控制、清屏、输入输出等基础功能
-
配置管理模块:
- 配置文件的加载和保存
- 配置参数的修改
-
数据持久化模块:
- CSV文件的读写
- 数据备份和恢复
-
停车场管理模块:
- 车辆进入和离开
- 车位分配和释放
- 费用计算
-
查询统计模块:
- 按车牌号查询
- 显示所有记录
- 统计信息
-
菜单系统模块:
- 主菜单和子菜单
- 用户交互界面
关键函数说明
vehicle_enter(): 车辆进入处理vehicle_exit(): 车辆离开处理calculate_fee(): 费用计算find_free_spot(): 查找空余车位update_free_spots(): 更新空余车位数量show_statistics(): 显示统计信息
源代码
/*
* 停车场管理系统 v1.0
*
* 功能说明:
* - 车辆进入停车场(记录车牌号、进入时间、分配车位)
* - 车辆离开停车场(计算停车费用、释放车位)
* - 查询停车场状态(空余车位、已停车位等)
* - 车辆信息查询和管理
* - 停车记录统计
* - 数据持久化(CSV格式)
* - 数据导入导出和备份功能
*
* 编译方法:
* Windows (MinGW): gcc parking_manager.c -o parking_manager.exe
* Linux/Mac: gcc parking_manager.c -o parking_manager
*
* 作者:系统自动生成
* 日期:2024
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#ifdef _WIN32
#include <windows.h>
#include <conio.h>
#else
#include <termios.h>
#include <unistd.h>
#endif
/* ==================== 常量定义 ==================== */
#define PLATE_LEN 20 // 车牌号最大长度
#define CAR_TYPE_LEN 20 // 车辆类型最大长度
#define MAX_PARKING_SPOTS 1000 // 最大停车位数量
#define MAX_RECORDS 10000 // 最大记录数量
#define DATA_FILE "parking_records.csv" // 默认数据文件
#define CONFIG_FILE "parking_config.txt" // 配置文件
#define BACKUP_DIR "backups" // 备份目录
/* 默认配置 */
#define DEFAULT_TOTAL_SPOTS 100 // 默认总车位
#define DEFAULT_FEE_PER_HOUR 5.0 // 默认每小时费用(元)
#define DEFAULT_FREE_MINUTES 15 // 默认免费时长(分钟)
/* ==================== 数据结构定义 ==================== */
/**
* 停车记录结构体
* 记录每辆车的停车信息
*/
typedef struct {
char plate[PLATE_LEN]; // 车牌号
char carType[CAR_TYPE_LEN]; // 车辆类型(如:小型车、大型车等)
int spotNumber; // 车位号
time_t enterTime; // 进入时间(时间戳)
time_t exitTime; // 离开时间(时间戳,0表示未离开)
double fee; // 停车费用
int isDeleted; // 删除标记(0=未删除,1=已删除)
} ParkingRecord;
/**
* 停车场配置结构体
* 存储停车场的配置信息
*/
typedef struct {
int totalSpots; // 总车位数量
int freeSpots; // 当前空余车位
double feePerHour; // 每小时费用(元)
int freeMinutes; // 免费时长(分钟)
} ParkingConfig;
/* ==================== 全局变量 ==================== */
ParkingRecord records[MAX_RECORDS]; // 停车记录数组
int record_count = 0; // 当前记录数量
ParkingConfig config; // 停车场配置
/* ==================== 颜色控制函数(Windows平台) ==================== */
#ifdef _WIN32
/**
* 设置控制台文字颜色(仅Windows平台)
* @param attr 颜色属性
*/
void set_color(WORD attr) {
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), attr);
}
#define COLOR_RESET (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE)
#define COLOR_RED (FOREGROUND_RED | FOREGROUND_INTENSITY)
#define COLOR_GREEN (FOREGROUND_GREEN | FOREGROUND_INTENSITY)
#define COLOR_YELLOW (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
#define COLOR_CYAN (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY)
#define COLOR_BLUE (FOREGROUND_BLUE | FOREGROUND_INTENSITY)
#else
// Linux/Mac平台的颜色控制(空实现)
void set_color(int attr) { (void)attr; }
#define COLOR_RESET 0
#define COLOR_RED 0
#define COLOR_GREEN 0
#define COLOR_YELLOW 0
#define COLOR_CYAN 0
#define COLOR_BLUE 0
#endif
/* ==================== 工具函数 ==================== */
/**
* 清屏函数(跨平台)
*/
void clear_screen() {
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
/**
* 暂停程序,等待用户按回车键
*/
void pause_anykey() {
printf("\n按回车键继续...");
getchar();
getchar();
}
/**
* 去除字符串末尾的换行符
* @param s 要处理的字符串
*/
void trim_newline(char *s) {
if (!s) return;
size_t n = strlen(s);
if (n > 0 && s[n-1] == '\n') {
s[n-1] = '\0';
}
}
/**
* 从标准输入读取一行文本
* @param buf 缓冲区
* @param sz 缓冲区大小
* @return 成功返回buf指针,失败返回NULL
*/
char* read_line(char *buf, size_t sz) {
if (!fgets(buf, (int)sz, stdin)) return NULL;
trim_newline(buf);
return buf;
}
/**
* 交互式读取整数输入
* @param prompt 提示信息
* @return 读取到的整数值
*/
int read_int(const char *prompt) {
char buf[64];
while (1) {
printf("%s", prompt);
if (!read_line(buf, sizeof(buf))) return 0;
if (strlen(buf) == 0) return 0;
char *end;
long v = strtol(buf, &end, 10);
if (end != buf && *end == '\0') return (int)v;
set_color(COLOR_RED);
printf("输入无效,请输入整数。\n");
set_color(COLOR_RESET);
}
}
/**
* 交互式读取浮点数输入
* @param prompt 提示信息
* @return 读取到的浮点数值
*/
double read_double(const char *prompt) {
char buf[64];
while (1) {
printf("%s", prompt);
if (!read_line(buf, sizeof(buf))) return 0.0;
if (strlen(buf) == 0) return 0.0;
char *end;
double v = strtod(buf, &end);
if (end != buf && *end == '\0') return v;
set_color(COLOR_RED);
printf("输入无效,请输入数字。\n");
set_color(COLOR_RESET);
}
}
/**
* 验证车牌号格式(简单验证:不能为空)
* @param plate 车牌号
* @return 有效返回1,无效返回0
*/
int validate_plate(const char *plate) {
if (strlen(plate) == 0 || strlen(plate) > PLATE_LEN - 1) return 0;
return 1;
}
/**
* 查找正在停车的记录(按车牌号,且未离开)
* @param plate 车牌号
* @return 找到返回索引,未找到返回-1
*/
int find_parking_by_plate(const char *plate) {
for (int i = 0; i < record_count; i++) {
if (records[i].isDeleted == 0 &&
records[i].exitTime == 0 && // 未离开
strcmp(records[i].plate, plate) == 0) {
return i;
}
}
return -1;
}
/**
* 查找可用的车位号
* @return 找到返回车位号(1开始),未找到返回-1
*/
int find_free_spot() {
// 标记已占用的车位
int occupied[MAX_PARKING_SPOTS] = {0};
for (int i = 0; i < record_count; i++) {
if (records[i].isDeleted == 0 && records[i].exitTime == 0) {
// 车辆还在停车场
if (records[i].spotNumber > 0 && records[i].spotNumber <= MAX_PARKING_SPOTS) {
occupied[records[i].spotNumber - 1] = 1;
}
}
}
// 查找第一个空余车位
for (int i = 0; i < config.totalSpots; i++) {
if (occupied[i] == 0) {
return i + 1; // 车位号从1开始
}
}
return -1; // 没有空余车位
}
/**
* 计算停车费用
* @param enterTime 进入时间(时间戳)
* @param exitTime 离开时间(时间戳)
* @return 停车费用(元)
*/
double calculate_fee(time_t enterTime, time_t exitTime) {
if (enterTime == 0 || exitTime == 0) return 0.0;
if (exitTime <= enterTime) return 0.0;
// 计算停车时长(秒)
double duration_seconds = difftime(exitTime, enterTime);
// 转换为分钟
double duration_minutes = duration_seconds / 60.0;
// 免费时长内不收费
if (duration_minutes <= config.freeMinutes) {
return 0.0;
}
// 计算费用(按小时计费,不足一小时按一小时计算)
double duration_hours = (duration_minutes - config.freeMinutes) / 60.0;
if (duration_hours < 0) duration_hours = 0;
// 向上取整到小时
int hours = (int)(duration_hours);
if (duration_hours > hours) hours++;
return hours * config.feePerHour;
}
/**
* 格式化时间显示
* @param time 时间戳
* @param buf 输出缓冲区
* @param size 缓冲区大小
*/
void format_time(time_t time, char *buf, size_t size) {
if (time == 0) {
strncpy(buf, "未记录", size - 1);
buf[size - 1] = '\0';
return;
}
struct tm *tm_info = localtime(&time);
strftime(buf, size, "%Y-%m-%d %H:%M:%S", tm_info);
}
/**
* 显示加载动画
*/
void show_loading() {
printf("\n");
for (int i = 0; i <= 20; i++) {
printf("\r系统加载中: [");
for (int j = 0; j < 20; j++) {
if (j < i) printf("=");
else printf(" ");
}
printf("] %.0f%%", i * 5.0);
fflush(stdout);
#ifdef _WIN32
Sleep(50);
#else
usleep(50000);
#endif
}
printf("\n");
clear_screen();
}
/**
* 打印分隔线
*/
void print_separator() {
printf("===========================================================\n");
}
/**
* 打印标题
* @param title 标题文本
*/
void print_title(const char *title) {
print_separator();
printf(" %s\n", title);
print_separator();
}
/* ==================== 函数前向声明 ==================== */
/**
* 更新空余车位数量(前向声明)
*/
void update_free_spots();
/* ==================== 配置管理函数 ==================== */
/**
* 初始化默认配置
*/
void init_default_config() {
config.totalSpots = DEFAULT_TOTAL_SPOTS;
config.freeSpots = DEFAULT_TOTAL_SPOTS;
config.feePerHour = DEFAULT_FEE_PER_HOUR;
config.freeMinutes = DEFAULT_FREE_MINUTES;
}
/**
* 加载配置文件
* @return 成功返回1,失败返回0
*/
int load_config() {
FILE *f = fopen(CONFIG_FILE, "r");
if (!f) {
// 文件不存在,使用默认配置
init_default_config();
return 0;
}
char line[256];
while (fgets(line, sizeof(line), f)) {
trim_newline(line);
if (strncmp(line, "totalSpots=", 11) == 0) {
config.totalSpots = atoi(line + 11);
} else if (strncmp(line, "feePerHour=", 11) == 0) {
config.feePerHour = atof(line + 11);
} else if (strncmp(line, "freeMinutes=", 12) == 0) {
config.freeMinutes = atoi(line + 12);
}
}
fclose(f);
// 计算当前空余车位
update_free_spots();
return 1;
}
/**
* 保存配置文件
* @return 成功返回1,失败返回0
*/
int save_config() {
FILE *f = fopen(CONFIG_FILE, "w");
if (!f) return 0;
fprintf(f, "totalSpots=%d\n", config.totalSpots);
fprintf(f, "feePerHour=%.2f\n", config.feePerHour);
fprintf(f, "freeMinutes=%d\n", config.freeMinutes);
fclose(f);
return 1;
}
/**
* 更新空余车位数量
*/
void update_free_spots() {
int occupied = 0;
for (int i = 0; i < record_count; i++) {
if (records[i].isDeleted == 0 && records[i].exitTime == 0) {
occupied++;
}
}
config.freeSpots = config.totalSpots - occupied;
}
/* ==================== 数据持久化函数 ==================== */
/**
* 保存数据到CSV文件
* @param filename 文件名
* @return 成功返回1,失败返回0
*/
int save_data(const char *filename) {
FILE *f = fopen(filename, "w");
if (!f) {
set_color(COLOR_RED);
perror("打开文件写入失败");
set_color(COLOR_RESET);
return 0;
}
// 写入CSV表头
fprintf(f, "车牌号,车辆类型,车位号,进入时间,离开时间,费用\n");
// 写入所有记录
for (int i = 0; i < record_count; i++) {
if (records[i].isDeleted) continue;
ParkingRecord *r = &records[i];
char enterTimeStr[64], exitTimeStr[64];
format_time(r->enterTime, enterTimeStr, sizeof(enterTimeStr));
format_time(r->exitTime, exitTimeStr, sizeof(exitTimeStr));
fprintf(f, "\"%s\",\"%s\",%d,\"%s\",\"%s\",%.2f\n",
r->plate, r->carType, r->spotNumber,
enterTimeStr, exitTimeStr, r->fee);
}
fclose(f);
return 1;
}
/**
* 从CSV文件加载数据
* @param filename 文件名
* @return 成功返回1,失败返回0
*/
int load_data(const char *filename) {
FILE *f = fopen(filename, "r");
if (!f) return 0;
char line[1024];
int line_no = 0;
record_count = 0;
while (fgets(line, sizeof(line), f) && record_count < MAX_RECORDS) {
line_no++;
trim_newline(line);
if (line[0] == '\0') continue;
// 跳过表头
if (line_no == 1 && (strstr(line, "车牌号") != NULL || strstr(line, "plate") != NULL)) {
continue;
}
// 解析CSV行
ParkingRecord r;
memset(&r, 0, sizeof(r));
// 简单的CSV解析
char *p = line;
int field = 0;
char *start = p;
int in_quotes = 0;
char fields[6][256] = {0};
while (*p && field < 6) {
if (*p == '"') {
in_quotes = !in_quotes;
} else if (*p == ',' && !in_quotes) {
*p = '\0';
while (*start == '"' || *start == ' ') start++;
char *end = p - 1;
while (end > start && (*end == '"' || *end == ' ')) end--;
*(end + 1) = '\0';
strncpy(fields[field], start, sizeof(fields[field]) - 1);
field++;
start = p + 1;
}
p++;
}
// 处理最后一个字段
if (field < 6 && *start) {
while (*start == '"' || *start == ' ') start++;
char *end = p - 1;
while (end > start && (*end == '"' || *end == ' ' || *end == '\n')) end--;
*(end + 1) = '\0';
strncpy(fields[field], start, sizeof(fields[field]) - 1);
}
// 填充记录
if (strlen(fields[0]) > 0) {
strncpy(r.plate, fields[0], PLATE_LEN - 1);
strncpy(r.carType, fields[1], CAR_TYPE_LEN - 1);
r.spotNumber = atoi(fields[2]);
// 解析时间(跨平台兼容)
struct tm tm_enter = {0}, tm_exit = {0};
if (strlen(fields[3]) > 0 && strcmp(fields[3], "未记录") != 0) {
// 使用sscanf解析时间字符串 "YYYY-MM-DD HH:MM:SS"
if (sscanf(fields[3], "%d-%d-%d %d:%d:%d",
&tm_enter.tm_year, &tm_enter.tm_mon, &tm_enter.tm_mday,
&tm_enter.tm_hour, &tm_enter.tm_min, &tm_enter.tm_sec) == 6) {
tm_enter.tm_year -= 1900; // tm_year是1900年以来的年数
tm_enter.tm_mon -= 1; // tm_mon是0-11
r.enterTime = mktime(&tm_enter);
}
}
if (strlen(fields[4]) > 0 && strcmp(fields[4], "未记录") != 0) {
if (sscanf(fields[4], "%d-%d-%d %d:%d:%d",
&tm_exit.tm_year, &tm_exit.tm_mon, &tm_exit.tm_mday,
&tm_exit.tm_hour, &tm_exit.tm_min, &tm_exit.tm_sec) == 6) {
tm_exit.tm_year -= 1900;
tm_exit.tm_mon -= 1;
r.exitTime = mktime(&tm_exit);
}
}
r.fee = atof(fields[5]);
r.isDeleted = 0;
records[record_count++] = r;
}
}
fclose(f);
update_free_spots();
return 1;
}
/**
* 备份数据文件
* @return 成功返回1,失败返回0
*/
int backup_data() {
time_t t = time(NULL);
struct tm *tm = localtime(&t);
char backup_file[256];
// 创建备份目录
#ifdef _WIN32
system("if not exist backups mkdir backups");
#else
system("mkdir -p backups");
#endif
snprintf(backup_file, sizeof(backup_file),
"%s/backup_%04d%02d%02d_%02d%02d%02d.csv",
BACKUP_DIR,
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
if (save_data(backup_file)) {
set_color(COLOR_GREEN);
printf("备份成功: %s\n", backup_file);
set_color(COLOR_RESET);
return 1;
} else {
set_color(COLOR_RED);
printf("备份失败\n");
set_color(COLOR_RESET);
return 0;
}
}
/* ==================== 停车场管理函数 ==================== */
/**
* 车辆进入停车场
*/
void vehicle_enter() {
if (config.freeSpots <= 0) {
set_color(COLOR_RED);
printf("停车场已满,无法进入!\n");
set_color(COLOR_RESET);
return;
}
if (record_count >= MAX_RECORDS) {
set_color(COLOR_RED);
printf("记录数量已达上限\n");
set_color(COLOR_RESET);
return;
}
print_title("车辆进入停车场");
ParkingRecord r;
memset(&r, 0, sizeof(r));
char buf[256];
// 输入车牌号
while (1) {
printf("车牌号*: ");
if (!read_line(buf, sizeof(buf))) return;
if (!validate_plate(buf)) {
set_color(COLOR_RED);
printf("车牌号不能为空,请重新输入\n");
set_color(COLOR_RESET);
continue;
}
// 检查是否已在停车场
if (find_parking_by_plate(buf) != -1) {
set_color(COLOR_RED);
printf("该车辆已在停车场内!\n");
set_color(COLOR_RESET);
return;
}
strncpy(r.plate, buf, PLATE_LEN - 1);
break;
}
// 输入车辆类型(可选)
printf("车辆类型 (如:小型车、大型车等,留空跳过): ");
read_line(buf, sizeof(buf));
strncpy(r.carType, buf, CAR_TYPE_LEN - 1);
// 分配车位
int spot = find_free_spot();
if (spot == -1) {
set_color(COLOR_RED);
printf("没有可用车位!\n");
set_color(COLOR_RESET);
return;
}
r.spotNumber = spot;
r.enterTime = time(NULL);
r.exitTime = 0;
r.fee = 0.0;
r.isDeleted = 0;
// 添加到记录
records[record_count++] = r;
update_free_spots();
set_color(COLOR_GREEN);
printf("\n车辆进入成功!\n");
printf("车牌号: %s\n", r.plate);
printf("车位号: %d\n", r.spotNumber);
char timeStr[64];
format_time(r.enterTime, timeStr, sizeof(timeStr));
printf("进入时间: %s\n", timeStr);
set_color(COLOR_RESET);
}
/**
* 车辆离开停车场
*/
void vehicle_exit() {
print_title("车辆离开停车场");
char plate[PLATE_LEN];
printf("输入车牌号: ");
read_line(plate, sizeof(plate));
int idx = find_parking_by_plate(plate);
if (idx == -1) {
set_color(COLOR_RED);
printf("未找到该车辆或车辆已离开!\n");
set_color(COLOR_RESET);
return;
}
ParkingRecord *r = &records[idx];
// 设置离开时间
r->exitTime = time(NULL);
// 计算费用
r->fee = calculate_fee(r->enterTime, r->exitTime);
// 显示信息
printf("\n车辆信息:\n");
printf("车牌号: %s\n", r->plate);
printf("车辆类型: %s\n", strlen(r->carType) > 0 ? r->carType : "未填写");
printf("车位号: %d\n", r->spotNumber);
char enterTimeStr[64], exitTimeStr[64];
format_time(r->enterTime, enterTimeStr, sizeof(enterTimeStr));
format_time(r->exitTime, exitTimeStr, sizeof(exitTimeStr));
printf("进入时间: %s\n", enterTimeStr);
printf("离开时间: %s\n", exitTimeStr);
// 计算停车时长
double duration_seconds = difftime(r->exitTime, r->enterTime);
int hours = (int)(duration_seconds / 3600);
int minutes = (int)((duration_seconds - hours * 3600) / 60);
printf("停车时长: %d小时%d分钟\n", hours, minutes);
set_color(COLOR_CYAN);
printf("停车费用: %.2f 元\n", r->fee);
set_color(COLOR_RESET);
// 释放车位
update_free_spots();
set_color(COLOR_GREEN);
printf("\n车辆离开成功!\n");
set_color(COLOR_RESET);
}
/**
* 查询停车场状态
*/
void show_parking_status() {
print_title("停车场状态");
printf("总车位: %d\n", config.totalSpots);
printf("已停车位: %d\n", config.totalSpots - config.freeSpots);
printf("空余车位: %d\n", config.freeSpots);
printf("使用率: %.1f%%\n",
config.totalSpots > 0 ?
(double)(config.totalSpots - config.freeSpots) * 100.0 / config.totalSpots : 0.0);
// 显示正在停车的车辆
int parking_count = 0;
for (int i = 0; i < record_count; i++) {
if (records[i].isDeleted == 0 && records[i].exitTime == 0) {
parking_count++;
}
}
if (parking_count > 0) {
printf("\n正在停车的车辆 (%d 辆):\n", parking_count);
printf("%-15s %-15s %-8s %-20s\n", "车牌号", "车辆类型", "车位号", "进入时间");
print_separator();
for (int i = 0; i < record_count; i++) {
if (records[i].isDeleted == 0 && records[i].exitTime == 0) {
ParkingRecord *r = &records[i];
char timeStr[64];
format_time(r->enterTime, timeStr, sizeof(timeStr));
printf("%-15s %-15s %-8d %-20s\n",
r->plate,
strlen(r->carType) > 0 ? r->carType : "-",
r->spotNumber,
timeStr);
}
}
}
}
/**
* 按车牌号查询车辆信息
*/
void query_by_plate() {
print_title("按车牌号查询");
char plate[PLATE_LEN];
printf("输入车牌号: ");
read_line(plate, sizeof(plate));
int found = 0;
for (int i = 0; i < record_count; i++) {
if (records[i].isDeleted == 0 && strcmp(records[i].plate, plate) == 0) {
found = 1;
ParkingRecord *r = &records[i];
printf("\n车辆信息:\n");
printf("车牌号: %s\n", r->plate);
printf("车辆类型: %s\n", strlen(r->carType) > 0 ? r->carType : "未填写");
printf("车位号: %d\n", r->spotNumber);
char enterTimeStr[64], exitTimeStr[64];
format_time(r->enterTime, enterTimeStr, sizeof(enterTimeStr));
format_time(r->exitTime, exitTimeStr, sizeof(exitTimeStr));
printf("进入时间: %s\n", enterTimeStr);
if (r->exitTime > 0) {
printf("离开时间: %s\n", exitTimeStr);
printf("停车费用: %.2f 元\n", r->fee);
} else {
printf("状态: 正在停车中\n");
// 计算当前停车时长
time_t now = time(NULL);
double duration_seconds = difftime(now, r->enterTime);
int hours = (int)(duration_seconds / 3600);
int minutes = (int)((duration_seconds - hours * 3600) / 60);
printf("已停车时长: %d小时%d分钟\n", hours, minutes);
}
break;
}
}
if (!found) {
set_color(COLOR_RED);
printf("未找到该车辆记录\n");
set_color(COLOR_RESET);
}
}
/**
* 显示所有停车记录
*/
void show_all_records() {
print_title("所有停车记录");
int count = 0;
for (int i = 0; i < record_count; i++) {
if (records[i].isDeleted == 0) count++;
}
if (count == 0) {
printf("暂无停车记录\n");
return;
}
printf("共 %d 条记录\n\n", count);
printf("%-15s %-12s %-8s %-20s %-20s %-10s\n",
"车牌号", "车辆类型", "车位号", "进入时间", "离开时间", "费用");
print_separator();
for (int i = 0; i < record_count; i++) {
if (records[i].isDeleted) continue;
ParkingRecord *r = &records[i];
char enterTimeStr[64], exitTimeStr[64];
format_time(r->enterTime, enterTimeStr, sizeof(enterTimeStr));
format_time(r->exitTime, exitTimeStr, sizeof(exitTimeStr));
printf("%-15s %-12s %-8d %-20s %-20s %-10.2f\n",
r->plate,
strlen(r->carType) > 0 ? r->carType : "-",
r->spotNumber,
enterTimeStr,
r->exitTime > 0 ? exitTimeStr : "停车中",
r->fee);
}
}
/**
* 统计功能
*/
void show_statistics() {
print_title("统计信息");
// 今日统计
time_t now = time(NULL);
struct tm *today = localtime(&now);
today->tm_hour = 0;
today->tm_min = 0;
today->tm_sec = 0;
time_t today_start = mktime(today);
int today_count = 0;
double today_income = 0.0;
int current_parking = 0;
double total_income = 0.0;
for (int i = 0; i < record_count; i++) {
if (records[i].isDeleted == 0) {
if (records[i].enterTime >= today_start) {
today_count++;
}
if (records[i].exitTime >= today_start && records[i].exitTime > 0) {
today_income += records[i].fee;
}
if (records[i].exitTime == 0) {
current_parking++;
}
if (records[i].exitTime > 0) {
total_income += records[i].fee;
}
}
}
printf("今日统计:\n");
printf(" 进入车辆: %d 辆\n", today_count);
printf(" 今日收入: %.2f 元\n", today_income);
printf("\n当前状态:\n");
printf(" 正在停车: %d 辆\n", current_parking);
printf(" 空余车位: %d 个\n", config.freeSpots);
printf("\n累计统计:\n");
printf(" 总收入: %.2f 元\n", total_income);
printf(" 总记录: %d 条\n", record_count);
}
/* ==================== 菜单系统 ==================== */
/**
* 停车场管理菜单
*/
void parking_management_menu() {
while (1) {
clear_screen();
#ifdef _WIN32
system("color 0B");
#endif
print_title("停车场管理");
printf("1. 车辆进入\n");
printf("2. 车辆离开\n");
printf("3. 查询停车场状态\n");
printf("0. 返回主菜单\n");
int c = read_int("\n请选择操作 (0-3): ");
switch (c) {
case 1: vehicle_enter(); pause_anykey(); break;
case 2: vehicle_exit(); pause_anykey(); break;
case 3: show_parking_status(); pause_anykey(); break;
case 0: return;
default:
set_color(COLOR_RED);
printf("无效选择\n");
set_color(COLOR_RESET);
pause_anykey();
break;
}
}
}
/**
* 查询菜单
*/
void query_menu() {
while (1) {
clear_screen();
#ifdef _WIN32
system("color 0B");
#endif
print_title("数据查询");
printf("1. 按车牌号查询\n");
printf("2. 显示所有记录\n");
printf("3. 统计信息\n");
printf("0. 返回主菜单\n");
int c = read_int("\n请选择操作 (0-3): ");
switch (c) {
case 1: query_by_plate(); pause_anykey(); break;
case 2: show_all_records(); pause_anykey(); break;
case 3: show_statistics(); pause_anykey(); break;
case 0: return;
default:
set_color(COLOR_RED);
printf("无效选择\n");
set_color(COLOR_RESET);
pause_anykey();
break;
}
}
}
/**
* 系统设置菜单
*/
void settings_menu() {
while (1) {
clear_screen();
#ifdef _WIN32
system("color 0B");
#endif
print_title("系统设置");
printf("1. 查看配置\n");
printf("2. 修改配置\n");
printf("3. 保存数据\n");
printf("4. 备份数据\n");
printf("5. 加载数据文件\n");
printf("6. 导出CSV文件\n");
printf("0. 返回主菜单\n");
int c = read_int("\n请选择操作 (0-6): ");
switch (c) {
case 1: {
printf("\n当前配置:\n");
printf("总车位: %d\n", config.totalSpots);
printf("每小时费用: %.2f 元\n", config.feePerHour);
printf("免费时长: %d 分钟\n", config.freeMinutes);
pause_anykey();
break;
}
case 2: {
printf("\n修改配置:\n");
int total = read_int("总车位数量: ");
if (total > 0 && total <= MAX_PARKING_SPOTS) {
config.totalSpots = total;
update_free_spots();
}
double fee = read_double("每小时费用 (元): ");
if (fee >= 0) {
config.feePerHour = fee;
}
int free = read_int("免费时长 (分钟): ");
if (free >= 0) {
config.freeMinutes = free;
}
save_config();
set_color(COLOR_GREEN);
printf("配置已保存\n");
set_color(COLOR_RESET);
pause_anykey();
break;
}
case 3:
if (save_data(DATA_FILE)) {
set_color(COLOR_GREEN);
printf("保存成功\n");
set_color(COLOR_RESET);
} else {
set_color(COLOR_RED);
printf("保存失败\n");
set_color(COLOR_RESET);
}
pause_anykey();
break;
case 4:
backup_data();
pause_anykey();
break;
case 5: {
char fname[260];
printf("输入要加载的文件名 (默认 %s): ", DATA_FILE);
read_line(fname, sizeof(fname));
if (strlen(fname) == 0) strcpy(fname, DATA_FILE);
if (load_data(fname)) {
set_color(COLOR_GREEN);
printf("加载成功,共 %d 条记录\n", record_count);
set_color(COLOR_RESET);
} else {
set_color(COLOR_RED);
printf("加载失败或文件不存在\n");
set_color(COLOR_RESET);
}
pause_anykey();
break;
}
case 6: {
char fname[260];
printf("导出文件名 (默认 %s): ", DATA_FILE);
read_line(fname, sizeof(fname));
if (strlen(fname) == 0) strcpy(fname, DATA_FILE);
if (save_data(fname)) {
set_color(COLOR_GREEN);
printf("导出成功: %s\n", fname);
set_color(COLOR_RESET);
} else {
set_color(COLOR_RED);
printf("导出失败\n");
set_color(COLOR_RESET);
}
pause_anykey();
break;
}
case 0: return;
default:
set_color(COLOR_RED);
printf("无效选择\n");
set_color(COLOR_RESET);
pause_anykey();
break;
}
}
}
/**
* 主菜单
*/
int main_menu() {
while (1) {
clear_screen();
#ifdef _WIN32
system("color 0B");
#endif
print_title("停车场管理系统 v1.0");
printf("1. 停车场管理\n");
printf("2. 数据查询\n");
printf("3. 系统设置\n");
printf("0. 退出系统\n");
int c = read_int("\n请选择操作 (0-3): ");
switch (c) {
case 1: parking_management_menu(); break;
case 2: query_menu(); break;
case 3: settings_menu(); break;
case 0: return 0;
default:
set_color(COLOR_RED);
printf("无效选择\n");
set_color(COLOR_RESET);
pause_anykey();
break;
}
}
}
/* ==================== 主函数 ==================== */
/**
* 程序入口
* @return 程序退出码
*/
int main() {
#ifdef _WIN32
system("color 0B");
#endif
// 显示加载动画
show_loading();
// 加载配置
if (load_config()) {
set_color(COLOR_GREEN);
printf("已加载配置文件\n");
set_color(COLOR_RESET);
} else {
init_default_config();
set_color(COLOR_YELLOW);
printf("使用默认配置\n");
set_color(COLOR_RESET);
}
// 尝试加载数据文件
if (load_data(DATA_FILE)) {
set_color(COLOR_GREEN);
printf("已加载数据文件 %s,共 %d 条记录\n", DATA_FILE, record_count);
set_color(COLOR_RESET);
} else {
set_color(COLOR_YELLOW);
printf("未找到数据文件,使用空数据\n");
set_color(COLOR_RESET);
}
pause_anykey();
// 进入主菜单
main_menu();
// 退出前询问是否保存
printf("\n是否保存数据到 %s ? (Y/N): ", DATA_FILE);
char ans[8];
read_line(ans, sizeof(ans));
if (ans[0] == 'Y' || ans[0] == 'y') {
if (save_data(DATA_FILE)) {
set_color(COLOR_GREEN);
printf("保存成功\n");
set_color(COLOR_RESET);
} else {
set_color(COLOR_RED);
printf("保存失败\n");
set_color(COLOR_RESET);
}
}
// 保存配置
save_config();
printf("\n感谢使用停车场管理系统!\n");
return 0;
}
祝您使用愉快!

1096

被折叠的 条评论
为什么被折叠?



