逆向软件工程实践

一、来源
车牌管理系统由同学2329317在C语言课程学习的过程中,结合课堂知识进行实践时所编写的一个系统。
二、运行环境及运行结果
运行环境为Dev-C++,运行结果见附图。

代码如下:

include <stdio.h>

include <stdlib.h>

include <string.h>

include <time.h>

define MAX_PLATE_NUMBERS 100

define MAX_USER_INFO 50

// 定义一个结构体来存储车牌号和用户信息
typedef struct {
char plateNumber[10];
char userInfo[MAX_USER_INFO];
} PlateInfo;

// 存储车牌信息的数组
PlateInfo allocatedPlates[MAX_PLATE_NUMBERS];
int allocatedCount = 0;

// 随机生成车牌号
void generateRandomPlate(char *plate) {
sprintf(plate, "%04d%04d", rand() % 10000, rand() % 10000);
}

// 检查车牌号是否已分配
int isPlateAllocated(char *plate) {
for (int i = 0; i < allocatedCount; i++) {
if (strcmp(allocatedPlates[i].plateNumber, plate) == 0) {
return 1; // 已分配
}
}
return 0; // 未分配
}

// 分配车牌号
void allocatePlate(char *plate, char *userInfo) {
if (allocatedCount < MAX_PLATE_NUMBERS && !isPlateAllocated(plate)) {
strcpy(allocatedPlates[allocatedCount].plateNumber, plate);
strcpy(allocatedPlates[allocatedCount].userInfo, userInfo);
allocatedCount++;
printf("车牌号 %s 已成功分配给用户 %s\n", plate, userInfo);
} else {
printf("车牌号已满或已存在,请重新生成。\n");
}
}

// 显示所有已分配的车牌号和用户信息
void displayAllocatedPlates() {
printf("已分配的车牌号和用户信息:\n");
for (int i = 0; i < allocatedCount; i++) {
printf("车牌号:%s, 用户信息:%s\n", allocatedPlates[i].plateNumber, allocatedPlates[i].userInfo);
}
}

int main() {
char choice;
char plate1[10], plate2[10];
char userInfo[MAX_USER_INFO];
int userChoice;
int ch;
srand((unsigned int)time(NULL)); // 初始化随机数生成器
printf("1. 生成车牌号\n");
printf("2. 查看已分配的车牌号\n");
printf("3. 退出\n");
printf("请选择一个选项:");
do
{
scanf("%c", &choice);
switch (choice) {
case '1':
generateRandomPlate(plate1);
generateRandomPlate(plate2);
if (!isPlateAllocated(plate1) && !isPlateAllocated(plate2)) {
printf("生成的车牌号:%s 和 %s\n", plate1, plate2);
printf("请选择一个车牌号并输入用户信息:");
scanf("%d", &ch);
scanf("%s",userInfo);
if(ch==1)
allocatePlate(plate1, userInfo); // 分配第一个车牌号
else
allocatePlate(plate2, userInfo); // 分配第二个车牌号
} else {
printf("生成的车牌号已被分配,请重新生成。\n");
}
break;
case '2':
displayAllocatedPlates();
break;
case '3':
printf("退出系统。\n");
break;
}
if(choice != '3'){
printf("请再次选择。\n");
}
} while (choice!='3');
return 0;
}
三、主要问题
在测试时发现了以下几个问题:

  1. 编译错误:缺少头文件或库
  2. 运行时错误:scanf 读取问题
  3. 逻辑错误:车牌号重复分配
  4. 数组越界错误:userInfo 的长度
  5. 文件读写错误
    四、更改后的结果
    代码如下:

include <stdio.h>

include <stdlib.h>

include <string.h>

include <time.h>

define MAX_PLATE_NUMBERS 100

define MAX_USER_INFO 50

define FILENAME "plates.txt"

// 定义一个结构体来存储车牌号和用户信息
typedef struct {
char plateNumber[10];
char userInfo[MAX_USER_INFO];
} PlateInfo;

// 存储车牌信息的数组
PlateInfo allocatedPlates[MAX_PLATE_NUMBERS];
int allocatedCount = 0;

// 随机生成车牌号
void generateRandomPlate(char* plate) {
plate[0] = 'A' + rand() % 26; // 随机生成第一个字母
plate[1] = 'A' + rand() % 26; // 随机生成第二个字母
sprintf(plate + 2, "%04d", rand() % 10000); // 随机生成四位数字
plate[6] = '\0'; // 结束字符串
}

// 检查车牌号是否已分配
int isPlateAllocated(char* plate) {
for (int i = 0; i < allocatedCount; i++) {
if (strcmp(allocatedPlates[i].plateNumber, plate) == 0) {
return 1; // 已分配
}
}
return 0; // 未分配
}

// 分配车牌号
void allocatePlate(char* plate, char* userInfo) {
if (allocatedCount < MAX_PLATE_NUMBERS) {
if (!isPlateAllocated(plate)) {
strncpy(allocatedPlates[allocatedCount].plateNumber, plate, 10);
strncpy(allocatedPlates[allocatedCount].userInfo, userInfo, MAX_USER_INFO - 1);
allocatedPlates[allocatedCount].userInfo[MAX_USER_INFO - 1] = '\0'; // 确保字符串结尾
allocatedCount++;
printf("\033[32m车牌号 %s 已成功分配给用户 %s\033[0m\n", plate, userInfo);
}
else {
printf("\033[31m车牌号已存在,请重新生成。\033[0m\n");
}
}
else {
printf("\033[31m分配已达上限。\033[0m\n");
}
}

// 显示所有已分配的车牌号和用户信息
void displayAllocatedPlates() {
printf("\n╔═════════════════════════════════╗\n");
printf("║ \033[34m已分配的车牌号信息如下:\033[0m ║\n");
printf("╚═════════════════════════════════╝\n");
if (allocatedCount == 0) {
printf("\033[33m暂无已分配车牌号。\033[0m\n");
}
else {
for (int i = 0; i < allocatedCount; i++) {
printf("车牌号:\033[36m%s\033[0m, 用户信息:\033[36m%s\033[0m\n", allocatedPlates[i].plateNumber, allocatedPlates[i].userInfo);
}
}
printf("===================================\n");
}

// 保存信息到文件
void saveToFile() {
FILE* file = fopen(FILENAME, "w");
if (file == NULL) {
perror("无法打开文件保存数据");
return;
}
for (int i = 0; i < allocatedCount; i++) {
fprintf(file, "%s %s\n", allocatedPlates[i].plateNumber, allocatedPlates[i].userInfo);
}
fclose(file);
}

// 从文件加载信息
void loadFromFile() {
FILE* file = fopen(FILENAME, "r");
if (file == NULL) {
printf("没有找到历史数据文件,将创建新的存储文件。\n");
return;
}
while (fscanf(file, "%s %s", allocatedPlates[allocatedCount].plateNumber, allocatedPlates[allocatedCount].userInfo) == 2) {
allocatedCount++;
}
fclose(file);
}

// 清屏功能
void clearScreen() {

ifdef _WIN32

system("cls");

else

system("clear");

endif

}

// 美化输出
void printMenu() {
printf("\n╔═════════════════════════════════╗\n");
printf("║ \033[1;32m欢迎使用车牌号分配系统\033[0m ║\n");
printf("╠═════════════════════════════════╣\n");
printf("║ \033[34m1. 生成车牌号 \033[0m║\n");
printf("║ \033[34m2. 查看已分配的车牌号 \033[0m║\n");
printf("║ \033[34m3. 退出 \033[0m║\n");
printf("╚═════════════════════════════════╝\n");
printf("请选择一个选项:");
}

// 获取用户选择(验证输入)
int getValidChoice() {
int choice;
while (1) {
printf("请输入1或2以选择车牌号:");
if (scanf("%d", &choice) == 1 && (choice == 1 || choice == 2)) {
return choice;
}
else {
printf("\033[31m无效输入,请重试。\033[0m\n");
while (getchar() != '\n'); // 清空输入缓冲区
}
}
}

int main() {
char choice;
char plate1[10], plate2[10];
char userInfo[MAX_USER_INFO];
int ch;

srand((unsigned int)time(NULL)); // 初始化随机数生成器
loadFromFile(); // 加载文件信息

do {
    clearScreen();
    printMenu();
    scanf(" %c", &choice); // 注意空格避免缓冲问题
    switch (choice) {
    case '1':
        generateRandomPlate(plate1);
        generateRandomPlate(plate2);
        if (!isPlateAllocated(plate1) && !isPlateAllocated(plate2)) {
            printf("生成的车牌号:\033[36m%s\033[0m 和 \033[36m%s\033[0m\n", plate1, plate2);
            printf("请选择一个车牌号(1 或 2)并输入用户信息:\n");
            ch = getValidChoice();
            scanf("%s", userInfo);
            if (ch == 1) {
                allocatePlate(plate1, userInfo);
            }
            else if (ch == 2) {
                allocatePlate(plate2, userInfo);
            }
            saveToFile(); // 保存信息
        }
        else {
            printf("\033[31m生成的车牌号已被分配,请重新生成。\033[0m\n");
        }
        break;
    case '2':
        displayAllocatedPlates();
        break;
    case '3':
        printf("\033[32m退出系统。\033[0m\n");
        break;
    default:
        printf("\033[31m无效输入,请重试。\033[0m\n");
    }
    if (choice != '3') {
        printf("\033[33m按回车继续...\033[0m\n");
        getchar(); // 捕获回车
        getchar();
    }
} while (choice != '3');
return 0;

}
五、重构后的运行结果
运行结果如下:



车牌信息文件:

六、总结
重要点在于车牌号的生成与唯一性检查。使用 rand() 函数生成随机车牌号时,我需要确保车牌号的格式和唯一性,这要求我在每次生成车牌号时,都要进行充分的检查。在实现时,利用了 strcmp() 比较函数对已分配的车牌号进行线性查找,虽然这种方法较为简单,但却确保了系统分配的车牌号不会重复,这对整个系统的稳定性至关重要。其次,文件操作的部分也是一个关键点。在程序设计时,我考虑到数据的持久化问题,因此在每次修改分配的车牌号时,我都会将数据保存到文件中,确保程序重启后能够恢复之前的分配状态。在这个过程中,打开文件、读取数据、写入数据等操作让我更好地理解了文件的读取与写入机制。
这次逆向软件工程的实践提升了我阅读并理解代码的能力,在理解过后能充分思考,指出其中的不足与缺陷,在改进的过程中,我自身的编程能力也会因此得到强化,我在这次实践中收益匪浅。

posted @ 2025-02-25 14:25  祺业  阅读(31)  评论(0)    收藏  举报