前置知识:c语言、数据结构、Linux操作系统
一、功能需求
1.信息录入
2.信息显示(按起飞时间先后顺序显示)
3.信息查询(可根据不同的关键字进行查询)
4.信息修改
5.信息删除
二、技术要求
1.使用链表对录入的信息进行存储
2.对录入的信息进行排序(可按起飞时间进行排序)
3.使用make管理编译工程项目
三、思路
1.使用双向循环链表
2.分文件编写:main.c:调用函数
flight.c:编写函数
flight.h:头文件
makefile:管理工程项目
3.需要在flight.h中定义保存航班信息的结构体
typedef struct Flight //航班信息数据结构
{
char number[10]; //航班号
char staddress[10]; //起点站
char arraddress[10]; //终点站
//char DATE[10]; //班期
char TYPE[10]; //机型
char stime[20]; //起飞时间
char atime[20]; //到达时间
int value; //票价
}Flight;
然后定义保存航班节点的结构体
typedef struct _Node //双向链表结点
{
Flight info;
struct _Node *next,*prev;
}node,*pnode;
四、代码
1.flight.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef struct Flight //航班信息数据结构
{
char number[10]; //航班号
char staddress[10]; //起点站
char arraddress[10]; //终点站
//char DATE[10]; //班期
char TYPE[10]; //机型
char stime[20]; //起飞时间
char atime[20]; //到达时间
int value; //票价
}Flight,*pFlight;
typedef struct _Node //双向链表结点
{
Flight info;
struct _Node *next,*prev;
}node,*pnode;
//初始化
pnode inithead(pnode);
// 函数声明
void insertFlight(pnode);
void display(pnode);
void searchFlight(pnode);
void modifyFlight(pnode);
void deleteFlight(pnode);
void sortFlights(pnode);
//打印特定航班信息
void flightprintf(pnode);
void saveFlightsToFile(pnode);
//打印系统首界面
void systemprintf();
2.flight.c
#include"flight.h"
//初始化
pnode inithead(pnode head){
pnode p = (pnode)malloc(sizeof(node));
if(p==NULL){
perror("malloc:");
return NULL;
}
head = p;
head->next = head;
head->prev = head;
return head;
}
//打印系统首界面
void systemprintf(){
printf("******************************\n");
printf("欢迎来到航班查询系统!!!\n");
printf("1. 录入航班信息\n");
printf("2. 显示航班信息\n");
printf("3. 查询航班信息\n");
printf("4. 修改航班信息\n");
printf("5. 删除航班信息\n");
printf("6. 按起飞时间排序\n");
printf("7. 保存数据到文件\n");
printf("0. 退出\n");
printf("请输入您的选择:\n");
printf("******************************\n");
}
// 插入操作(尾插)
getchar()函数:用于吸收用户输入的回车键
void insertFlight(pnode head) {
pnode newFlight = (pnode)malloc(sizeof(node));
if (newFlight == NULL) {
printf("航班信息录入失败!\n");
perror("malloc:");
return;
}
printf("请输入航班号:");
scanf("%s", newFlight->info.number);
printf("请输入起点站:");
scanf("%s", newFlight->info.staddress);
printf("请输入终点站:");
scanf("%s", newFlight->info.arraddress);
printf("请输入机型:");
scanf("%s", newFlight->info.TYPE);
printf("请输入起飞时间:");
scanf("%s", newFlight->info.stime);
printf("请输入到达时间:");
scanf("%s", newFlight->info.atime);
int temp;
U:
printf("请输入票价(元):");
temp = scanf("%d", &newFlight->info.value);
if(temp!=1){
printf("格式有误!");
getchar();
goto U; //如果用户输入票价为非整数,让他重新输入。
}
//将该节点插入到链表尾端
head->prev->next = newFlight;
newFlight->prev = head->prev;
newFlight->next = head;
head->prev = newFlight;
printf("航班信息录入成功!\n");
}
//打印特定航班信息
void flightprintf(pnode current){
printf("航班号:%s\n", current->info.number);
printf("起点站:%s\n", current->info.staddress);
printf("终点站:%s\n", current->info.arraddress);
printf("机型:%s\n", current->info.TYPE);
printf("起飞时间:%s\n", current->info.stime);
printf("到达时间:%s\n", current->info.atime);
printf("票价:%d\n", current->info.value);
printf("--------------------\n");
}
// 遍历操作
从首节点(即保存数据的第一个节点,也是头节点的下一个节点)开始遍历,因为头节点不保存航班信息。然后调用上面的flightprintf()函数打印相应信息。
void display(pnode head) {
if (head->next == head) {
printf("暂无航班信息!\n");
return;
}
printf("--------------------\n");
printf("航班信息如下:\n");
pnode current = head->next;
while (current!=head){
flightprintf(current);
current = current->next;
}
printf("\n");
}
// 查询操作
让用户自行输入关键字,然后遍历,和节点中保存的航班信息中的每一项进行比对,一样则打印出该节点下的所有航班信息。
void searchFlight(pnode head) {
if (head->next == head) {
printf("暂无航班信息!\n");
return;
}
char word[20];
printf("请输入查询关键字:");
scanf("%s", word);
pnode current = head->next;
int found = 0;
while (current != head){
if (strstr(current->info.number, word) || strstr(current->info.staddress, word)||
strstr(current->info.arraddress, word) || strstr(current->info.TYPE, word)||
strstr(current->info.stime, word)|| strstr(current->info.atime, word)||
(atoi(word)==current->info.value)){
printf("**********已为您搜索到如下信息**********\n");
flightprintf(current);
found = 1;
}
current = current->next;
}
if (!found) {
printf("未找到符合条件的航班信息!\n");
}
}
// 修改操作
让用户自行输入航班号对特定节点下的航班信息进行修改。利用printf()函数打印出修改界面,用switch()让用户可以单独修改某项。
void modifyFlight(pnode head) {
if (head->next == head) {
printf("暂无航班信息!\n");
return;
}
char flightNumber[20];
printf("请输入要修改的航班号:");
scanf("%s", flightNumber);
pnode current = head->next;
int found = 0;
while (current != head);
{
if (strcmp(current->info.number, flightNumber) == 0) {
found = 1;
int choice;
while (1) {
printf("*************修改界面*************\n");
printf("1. 修改航班号\n");
printf("2. 修改起点站\n");
printf("3. 修改终点站\n");
printf("4. 修改机型\n");
printf("5. 修改起飞时间\n");
printf("6. 修改到达时间\n");
printf("7. 修改票价\n");
printf("0. 退出\n");
printf("请输入您的选择:\n");
printf("******************************\n");
scanf("%d", &choice);
switch (choice) {
case 0:
printf("bye bye!\n");
//exit(0);
return;
case 1:
printf("请输入新的航班号\n");
scanf("%s", current->info.number);
printf("修改成功!\n");
break;
case 2:
printf("请输入新的起点站\n");
scanf("%s", current->info.staddress);
printf("修改成功!\n");
break;
case 3:
printf("请输入新的终点站\n");
scanf("%s", current->info.arraddress);
printf("修改成功!\n");
break;
case 4:
printf("请输入新的机型\n");
scanf("%s", current->info.TYPE);
printf("修改成功!\n");
break;
case 5:
printf("请输入新的起飞时间\n");
scanf("%s", current->info.stime);
printf("修改成功!\n");
break;
case 6:
printf("请输入新的到达时间\n");
scanf("%s", current->info.atime);
printf("修改成功!\n");
break;
case 7:
printf("请输入新的票价\n");
scanf("%d", ¤t->info.value);
printf("修改成功!\n");
break;
default:
printf("无效的选择,请重新输入!\n");
break;
}
}
}
current = current->next;
}
if (!found) {
printf("未找到航班号为%s的航班信息!\n", flightNumber);
}
}
//删除操作
根据输入的航班号进行删除,即连接被删除节点的前后节点,记得释放被删除节点。
void deleteFlight(pnode head) {
if (head->next == head) {
printf("暂无航班信息!\n");
return;
}
char flightNumber[20];
printf("请输入要删除的航班号:");
scanf("%s", flightNumber);
pnode current = head->next;
int found = 0;
while (current != head)
{
if (strcmp(current->info.number, flightNumber) == 0) {
pnode prev = current->prev;
pnode next = current->next;
prev->next = next;
next->prev = prev;
free(current);
printf("航班信息删除成功!\n");
found = 1;
break;
}
current = current->next;
}
if (!found) {
printf("未找到航班号为%s的航班信息!\n", flightNumber);
}
}
//按起飞时间排序
在C语言中,atoi函数是一个字符串转换函数,其功能是将一个字符串转换为对应的整数。
函数原型如下:
int atoi(const char *str);
参数str是一个指向以null结尾的字符串的指针,表示要转换的字符串。
函数返回值是转换后的整数。
atoi函数的工作原理如下:
1. 从字符串的开头开始,跳过所有的空格字符。
2. 如果字符串中的第一个非空格字符是正号或负号,将其保存下来,并将指针向后移动一位。
3. 从当前位置开始,将连续的数字字符转换为整数,直到遇到非数字字符或字符串结尾。
4. 将转换后的整数返回。
需要注意的是,atoi函数存在一些限制和风险:
1. 如果字符串无法转换为整数,或者字符串为空,atoi函数将返回0。因此,在使用atoi函数之前,需要确保字符串的有效性。
2. atoi函数只能处理整数,无法处理小数或其他非整数类型。
3. 如果字符串表示的整数超出了int类型的范围,将导致溢出错误。
4. atoi函数不提供错误处理机制,因此在使用时需要注意异常情况的处理。
为了更安全和可靠地进行字符串到整数的转换,可以使用更强大的函数,如strtol或sscanf,它们提供了更多的错误处理和类型转换选项。
void sortFlights(pnode head) {
if (head->next == head) {
printf("暂无航班信息!\n");
return;
}
//只有一条航班信息的情况
if(head->next->next==head){
printf("航班信息按起飞时间排序成功!\n");
return;
}
int swapped = 1;
pnode current;
pnode last = head->prev;
while (swapped)
{
swapped = 0;
current = head->next;
while (current!= last) {
if (atoi(current->info.stime)>atoi(current->next->info.stime)) {
Flight temp = current->info;
current->info = current->next->info;
current->next->info = temp;
swapped = 1;
}
current = current->next;
}
last = current->prev;
}
printf("航班信息按起飞时间排序成功!\n");
}
下面是对代码的详细解释:
-
首先,函数接受一个指向链表头节点的指针作为参数。
-
如果链表为空,即头节点的下一个节点指向自身,输出"暂无航班信息!"并返回。
-
如果链表中只有一个节点,即头节点的下一个节点的下一个节点指向自身,输出"航班信息按起飞时间排序成功!"并返回。
-
声明一个整型变量swapped并初始化为1,用于标记是否进行了交换操作。
-
声明两个指针变量current和last,分别指向当前节点和最后一个节点。
-
进入一个while循环,条件为swapped的值。
-
将swapped的值设为0,表示当前循环不进行交换。
-
将current指针指向头节点的下一个节点。
-
进入一个嵌套的while循环,条件为current不等于last。
-
在内层循环中,比较current节点和current的下一个节点的起飞时间。
-
如果current节点的起飞时间大于current的下一个节点的起飞时间,执行交换操作。
-
创建一个临时变量temp,将current节点的航班信息赋值给temp。
-
将current节点的航班信息替换为current的下一个节点的航班信息。
-
将current的下一个节点的航班信息替换为temp。
-
将swapped的值设为1,表示进行了交换操作。
-
将current指针指向下一个节点。
-
内层循环结束后,将last指针指向current的前一个节点。
-
外层循环结束后,输出"航班信息按起飞时间排序成功!"。
总结:这段代码通过冒泡排序的方式对链表中的航班信息按照起飞时间进行排序,直到链表中所有的航班信息都按照起飞时间从小到大排列。
//保存到文件(扩展)
void saveFlightsToFile(pnode head) {
if (head->next == head) {
printf("暂无航班信息!\n");
return;
}
FILE* file = fopen("flights.txt", "w");
if (file == NULL) {
printf("无法打开文件!\n");
return;
}
pnode current = head->next;
while (current != head)
{
fprintf(file, "航班号:%s\n", current->info.number);
fprintf(file,"起点站:%s\n", current->info.staddress);
fprintf(file, "终点站:%s\n", current->info.arraddress);
fprintf(file,"机型:%s\n", current->info.TYPE);
fprintf(file, "起飞时间:%s\n", current->info.stime);
fprintf(file,"到达时间:%s\n", current->info.atime);
fprintf(file,"票价:%d\n", current->info.value);
fprintf(file, "--------------------------\n");
current = current->next;
}
fclose(file);
printf("航班信息已保存到文件flights.txt中!!!\n");
}
这段代码是一个将航班信息保存到文件的函数。下面是对代码的详细解释:
1. 首先,函数接受一个指向链表头节点的指针作为参数。
2. 如果链表为空,即头节点的下一个节点指向自身,输出"暂无航班信息!"并返回。
3. 创建一个指向文件的指针变量file,并使用fopen函数以写入模式打开名为"flights.txt"的文件。
4. 如果文件打开失败,即file指针为NULL,输出"无法打开文件!"并返回。
5. 将current指针指向头节点的下一个节点。
6. 进入一个while循环,条件为current不等于头节点。
7. 在循环中,使用fprintf函数将航班信息按照指定的格式写入文件。
8. 分别写入航班号、起点站、终点站、机型、起飞时间、到达时间和票价。
9. 每写入一条航班信息后,使用fprintf函数写入分隔符"--------------------------"。
10. 将current指针指向下一个节点。
11. 循环结束后,使用fclose函数关闭文件。
12. 输出"航班信息已保存到文件flights.txt中!!!"。
总结:这段代码通过打开一个文件,并将链表中的航班信息按照指定的格式写入文件,最后关闭文件。
3.main.c
system("clear"); ----->调用系统函数,进行清屏操作,需要在头文件里包含#include<stdlib.h>
#include"flight.h"
int main() {
int choice,temp;
// 全局变量,链表头指针
pnode head = NULL;
//调用初始化函数
head = inithead(head);
while (1) {
//调用打印系统首界面函数
systemprintf();
P:
temp = scanf("%d", &choice);
//防呆
if(temp!=1)
{
getchar();
printf("格式有误!请重新输入\n");
goto P;
}
//选择
switch (choice) {
case 0:
system("clear"); //清屏
printf("bye bye!\n");
exit(0); //结束进程
case 1:
system("clear");
insertFlight(head);
break;
case 2:
system("clear");
display(head);
break;
case 3:
searchFlight(head);
break;
case 4:
modifyFlight(head);
break;
case 5:
deleteFlight(head);
break;
case 6:
system("clear");
sortFlights(head);
break;
case 7:
saveFlightsToFile(head);
break;
default:
printf("无效的选择,请重新输入!\n");
}
}
return 0;
}
4.makefile
这是一个简单的Makefile文件,用于编译和链接C语言程序。
clean:flight
@rm *.o
@ls
flie=main.o flight.o
flight:$(flie)
@gcc $(flie) -o flight
main.o:main.c
@gcc -c main.c -o main.o
flight.o:flight.c
@gcc -c flight.c -o flight.o
这段makefile文件用于编译生成一个名为flight的可执行程序。下面是对文件内容的详解:
1. clean:flight
- 这是一个目标(target),表示要执行的操作是clean和flight。
- clean是一个伪目标,通常用于清理文件。
- @rm *.o 是clean目标的命令,用于删除所有以.o结尾的文件。
- @ls 是clean目标的命令,用于列出当前目录下的文件。
2. flie=main.o flight.o
- 这是一个变量定义,将main.o和flight.o赋值给变量flie。
3. flight:$(flie)
- 这是一个目标,表示要执行的操作是flight和$(flie)。
- $(flie)是变量引用,表示依赖于变量flie中定义的文件。
- @gcc $(flie) -o flight 是flight目标的命令,用于将$(flie)中的文件编译为flight可执行文件。
4. main.o:main.c
- 这是一个目标,表示要执行的操作是main.o和main.c。
- main.c是依赖文件,表示需要使用main.c来生成main.o。
- @gcc -c main.c -o main.o 是main.o目标的命令,用于将main.c编译为main.o目标文件。
5. flight.o:flight.c
- 这是一个目标,表示要执行的操作是flight.o和flight.c。
- flight.c是依赖文件,表示需要使用flight.c来生成flight.o。
- @gcc -c flight.c -o flight.o 是flight.o目标的命令,用于将flight.c编译为flight.o目标文件。
总结:这个makefile文件定义了几个目标和变量,用于编译生成一个名为flight的可执行文件。clean目标用于清理文件,flight目标用于编译生成可执行文件,main.o和flight.o目标用于编译生成目标文件。
我的另一篇文章