《C Primer Plus》编程练习—第14章

目录

  • 《C Primer Plus》编程练习
    • 第14章
      • 1.exercise1.c
      • 2.exercise2.c
      • 3.exercise3.c
      • 4.exercise4.c
      • 5.exercise5.c
      • 6.exercise6.c
      • 7.exercise7.c
      • 8.exercise8.c
      • 9.exercise9.c
      • 10.exercise10.c
      • 11.exercise11.c
      • 12.问题总结
        • 1.输入验证问题
        • 2.文件问题
        • 3.指针问题

《C Primer Plus》编程练习

第14章

1.exercise1.c

设计一个结构模板存储一个月份名、该月份名的3个字母缩写、该月的天数以及月份号。定义一个数组,内含12个结构并初始化为一个年份(非闰年)。假设在所有函数的外部声明了该结构模板和一个该结构类型的数组。编写一个函数,用户提供月份号,该函数就返回一年中到该月为止(包括该月)的总天数。用月份名的拼写代替月份号(别忘了使用strcmp())。在一个简单的程序中测试该函数。

//exercise14.1
#include 
#include 
#include 

struct month {
    char name[20];
    char abbrev[4];
    int days;
    int monumb;
};//结构模板 
const struct month months[12] = {
    {"January", "Jan", 31, 1},
    {"February", "Feb", 28, 2},
    {"March", "Mar", 31, 3},
    {"April", "Apr", 30, 4},
    {"May", "May", 31, 5},
    {"June", "Jun", 30, 6},
    {"July", "Jul", 31, 7},
    {"August", "Aug", 31, 8},
    {"September", "Sep", 30, 9},
    {"October", "Oct", 31, 10},
    {"November", "Nov", 3, 11},
    {"December", "Dec", 31, 12}
};//结构数组 
int days(char * input);//获取月份名,计算天数 
int main(void)
{
    char input[20];
    int total_days;

    printf("请输入月份名:(按q退出程序)\n");
    while (scanf("%s",input) == 1 && input[0] != 'q')//输入q才停止循环 
    {
        total_days = days(input);//调用函数 
        if (total_days > 0) //总天数大于0,说明有对应月份号 
        {
            printf("总天数是:%d\n",total_days);
        }
        else 
        {
            printf("输入有误。\n");
        }
        printf("请输入下一个月份名:(按q退出程序)\n");
    }
    printf("程序结束\n");

    return 0;
}
int days(char * input)
{
    int i;
    int total = 0;
    int num = 0;

    input[0] = toupper(input[0]);//第一个字母转成大写 
    for (i = 1; input[i] != '\0'; i++) //循环到输入为止,都转为小写字母 
    {
        input[i] = tolower(input[i]);
    }
    for (i = 0; i < 12; i++) 
    {
        if (strcmp(input, months[i].name) == 0) //和结构数组每一个元素的名字比较 
        {
            num = months[i].monumb;//找到对应月份,把月份号赋值给num 
            break;
        }
    }
    if (num == 0) //没有对应月份号 
    {
        total = 0;
    }
    else 
    {
        for (i = 0; i < num; i++) //有对应月份号 
        {
            total += months[i].days;//求和 
        }
    }
    return total;//返回总天数 
}

输入示例:

请输入月份名:(按q退出程序)
febRuary
总天数是:59
请输入下一个月份名:(按q退出程序)
q
程序结束

2.exercise2.c

编写一个函数,提示用户输入日、月和年。月份可以是月份号、月份名或月份名缩写。然后该程序应返回一年中到用户指定日子(包括这一天)的总天数。

//exercise14.2
#include 
#include 
#include 

struct month {
    char name[20];
    char abbrev[4];
    int days;
    int monumb;
};//结构模板 
struct month months[12] = {
    {"January", "Jan", 31, 1},
    {"February", "Feb", 28, 2},
    {"March", "Mar", 31, 3},
    {"April", "Apr", 30, 4},
    {"May", "May", 31, 5},
    {"June", "Jun", 30, 6},
    {"July", "Jul", 31, 7},
    {"August", "Aug", 31, 8},
    {"September", "Sep", 30, 9},
    {"October", "Oct", 31, 10},
    {"November", "Nov", 3, 11},
    {"December", "Dec", 31, 12}
};//结构数组 
int days(int day, char * input);//获取月份名,计算天数
void leapyear(int year); 
int main(void)
{
	int day, year;
    char input[20];
    int total_days;

    printf("请输入日,月份名,年:(按q退出程序)\n");
    while (scanf("%d %s %d", &day, input, &year) == 3 && input[0] != 'q') 
    {
    	leapyear(year);
        total_days = days(day, input);//调用函数 
        if (total_days > 0) //总天数大于0,说明有对应月份号 
        {
            printf("总天数是:%d\n",total_days);
        }
        else 
        {
            printf("输入有误。\n");
        }
        months[1].days = 28;//将2月份重置为28天 
        printf("请输入下一个日,月份名,年:(按q退出程序)\n");
    }
    printf("程序结束\n");

    return 0;
}
int days(int day, char * input)
{
    int i;
    int total = 0;
    int num = 0;

    input[0] = toupper(input[0]);//第一个字母转成大写 
    for (i = 1; input[i] != '\0'; i++) //循环到输入为止,都转为小写字母 
    {
        input[i] = tolower(input[i]);
    }
    for (i = 0; i < 12; i++) 
    {
        if (strcmp(input, months[i].name) == 0) //和结构数组每一个元素的名字比较 
        {
            num = months[i].monumb;//找到对应月份,把月份号赋值给num 
            break;
        }
    }
    if (num == 0) //没有对应月份号 
    {
        total = 0;
    }
    else 
    {
        for (i = 0; i < num; i++) //有对应月份号 
        {
            total += months[i].days;//求和 
        }
        total += day;//再加上日期数 
    }
    return total;//返回总天数 
}
void leapyear(int year)
{
	if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)//判断是否为闰年 
	{
		months[1].days = 29;//闰年2月为29天 
	}
}

输出示例:

请输入日,月份名,年:(按q退出程序)
1 march 2020
总天数是:92
请输入下一个日,月份名,年:(按q退出程序)
1 march 2019
总天数是:91
请输入下一个日,月份名,年:(按q退出程序)
q
程序结束

没有做过多的输入验证,默认是按照正常输入。

3.exercise3.c

修改程序manybook.c中的图书目录程序,使其按照输入图书的顺序输出图书的信息,然后按照书名的字母顺序输出图书的信息,最后按照价格的升序输出图书的信息。

//exercise14.3
#include 
#include 

char * s_gets(char * st, int n);
#define MAXTITL   40
#define MAXAUTL   40
#define MAXBKS   100              /* 书籍的最大数量  */

struct book {                     /* 建立book模板   */
    char title[MAXTITL];
    char author[MAXAUTL];
    float value;
};
void print(struct book library[MAXBKS], int count);//原样打印 
void print_al(struct book library[MAXBKS], int count);//按字母顺序打印 
void print_price(struct book library[MAXBKS], int count);//按价格升序打印 
int main(void)
{
    struct book library[MAXBKS]; /* book类型的结构数组 */
    int count = 0;
    
    printf("请输入书名:\n");
    printf("在开始处按enter键结束程序。\n");
    while (count < MAXBKS && s_gets(library[count].title, MAXTITL) != NULL
           && library[count].title[0] != '\0')
    {
        printf("输入作者名:\n");
        s_gets(library[count].author, MAXAUTL);
        printf("输入书的价格\n");
        scanf("%f", &library[count++].value);
        while (getchar() != '\n')
            continue;          /* 清理输入行  */
        if (count < MAXBKS)
            printf("输入下一本书的名字:\n");
    }
    
    if (count > 0)
    {
    	printf("这是你的书的顺序:\n");
        print(library, count);
        print_al(library, count);
        print_price(library, count);
    }
    else
    	printf("没有书。\n");
    
    return 0;
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');   // 查找换行符 
        if (find)                  // 如果地址不是NULL,
            *find = '\0';          // 在此处放置一个空字符 
        else
            while (getchar() != '\n')
                continue;          // 处理输入行中剩余的字符 
    }
    return ret_val;
}
void print(struct book library[MAXBKS], int count)
{
	int index;
    
	for (index = 0; index < count; index++)
	{
		printf("%s by %s: $%.2f\n", library[index].title,
                   library[index].author, library[index].value);
	}        
}
void print_al(struct book library[MAXBKS], int count)
{
	struct book temp;//标记 
	int index;
    int i;
    
    printf("书按字母顺序输出:\n");
	for (index = 0; index < count - 1; index++)//冒泡排序 
    {
        for (i = index + 1; i < count; i++)
        {
        	if (strcmp(library[index].title, library[i].title) > 0)
				//后面的书的字母排在前面的书的前面 
			{
				temp = library[index];//交换 
				library[index] = library[i];
				library[i] = temp;
			}
		}
	}
	print(library, count);//打印排序后的 
}
void print_price(struct book library[MAXBKS], int count)
{
	struct book temp;
	int index;
    int i;
    
    printf("书按价格顺序输出:\n");
	for (index = 0; index < count - 1; index++)
    {
        for (i = index + 1; i < count; i++)
        {
        	if (library[index].value > library[i].value)
			//前面的书价格高于后面的书 
			{
				temp = library[index];
				library[index] = library[i];
				library[i] = temp;
			}
		}
	}
	print(library, count);
}

输出示例:

请输入书名:
在开始处按enter键结束程序。
c study
输入作者名:
sttphen
输入书的价格
56
输入下一本书的名字:
python
输入作者名:
peter
输入书的价格
23
输入下一本书的名字:
java
输入作者名:
lily
输入书的价格
35
输入下一本书的名字:

这是你的书的顺序:
c study by sttphen: $56.00
python by peter: $23.00
java by lily: $35.00
书按字母顺序输出:
c study by sttphen: $56.00
java by lily: $35.00
python by peter: $23.00
书按价格顺序输出:
python by peter: $23.00
java by lily: $35.00
c study by sttphen: $56.00

4.exercise4.c

编写一个程序,创建一个有两个成员的结构模板:
a. 第1个成员是社会保险号,第2个成员是一个有3个成员的结构,第1个成员代表名,第2个成员代表中间名,第3个成员表示姓。创建并初始化一个内含5个该类型结构的数组。该程序以下面的格式打印数据:
Dribble, Flossie M. – 302039823
如果有中间名,只打印它的第1个字母,后面加一个点(.); 如果没有中间名,则不用打印点。编写一个程序进行打印,把结构数组传递给这个函数。
b. 修改a部分,传递结构的值而不是结构的地址。

//exercise14.4a
#include 
#include  
#define LEN 20

struct names {
	char fname[LEN];
	char mname[LEN];
	char lname[LEN];
};

struct message {
	char sonum[LEN];
	struct names name; 
};
char * s_gets(char * st, int n); 
void print(const struct message num[], int n);
int main(void)
{
	struct message num[5];//结构数组 
	int count = 0;
	
	printf("请输入保险号:\n");
    printf("在开始处按enter键结束程序。\n");
    while (count < 5 && s_gets(num[count].sonum, LEN) && num[count].sonum[0] != '\0')
    {//输入不超过5个,或者直接输入enter退出 
    	printf("请输入名:\n");
    	s_gets(num[count].name.fname, LEN);
    	printf("请输入中间名:\n");
    	s_gets(num[count].name.mname, LEN);
    	printf("请输入姓:\n");
    	s_gets(num[count].name.lname, LEN);
    	if (count < 5)//输入不超过5个,继续输入 
    	{	
    		printf("请输入下一个保险号:\n");
		}
		count++;
	}
	if (count > 0)//输入了成员 
	{
		printf("以下是所有成员:\n");
		print(num, count);	
	}
	else//没输入成员 
	{
		printf("没有成员。\n");
	}
	printf("程序结束。\n");
	
	return 0;
}
char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');   // 查找换行符 
        if (find)                  // 如果地址不是NULL,
            *find = '\0';          // 在此处放置一个空字符 
        else
            while (getchar() != '\n')
                continue;          // 处理输入行中剩余的字符 
    }
    return ret_val;
}
void print(const struct message num[], int n)
{
	int i;
	
	for (i = 0; i < n; i++)
	{
		if (num[i].name.mname[0] == '\0')//中间名是空 
		{
			printf("%s,%s . -- %s\n", num[i].name.fname, num[i].name.lname, num[i].sonum);
		}		 
		else
		{
			printf("%s,%s %c. -- %s\n", num[i].name.fname, num[i].name.lname, num[i].name.mname[0], num[i].sonum); 
		}
	}
}

输出示例:

请输入保险号:
在开始处按enter键结束程序。
302039823
请输入名:
Dribble
请输入中间名:
Mit
请输入姓:
Flossie
请输入下一个保险号:
845145656
请输入名:
san
请输入中间名:

请输入姓:
zhang
请输入下一个保险号:
123456789
请输入名:
er
请输入中间名:
xiao
请输入姓:
li
请输入下一个保险号:
963852741
请输入名:
wu
请输入中间名:
xiao
请输入姓:
wang
请输入下一个保险号:
159263487
请输入名:
qi
请输入中间名:

请输入姓:
tian
请输入下一个保险号:
以下是所有成员:
Dribble,Flossie M. -- 302039823
san,zhang . -- 845145656
er,li x. -- 123456789
wu,wang x. -- 963852741
qi,tian . -- 159263487
程序结束。
//exercise14.4b
#include 
#include  
#define LEN 20

struct names {
	char fname[LEN];
	char mname[LEN];
	char lname[LEN];
};

struct message {
	char sonum[LEN];
	struct names name; 
};
char * s_gets(char * st, int n); 
void print(const struct message nu);//传递结构 
int main(void)
{
	struct message num[5];//结构数组 
	int count = 0;
	int i; 
	
	printf("请输入保险号:\n");
    printf("在开始处按enter键结束程序。\n");
    while (count < 5 && s_gets(num[count].sonum, LEN) && num[count].sonum[0] != '\0')
    {//输入不超过5个,或者直接输入enter退出 
    	printf("请输入名:\n");
    	s_gets(num[count].name.fname, LEN);
    	printf("请输入中间名:\n");
    	s_gets(num[count].name.mname, LEN);
    	printf("请输入姓:\n");
    	s_gets(num[count].name.lname, LEN);
    	if (count < 5)//输入不超过5个,继续输入 
    	{	
    		printf("请输入下一个保险号:\n");
		}
		count++;
	}
	if (count > 0)//输入了成员 
	{
		printf("以下是所有成员:\n");
		for (i = 0; i < count; i++)
		{
			print(num[i]);//单独打印每个成员 
		}	
	}
	else//没输入成员 
	{
		printf("没有成员。\n");
	}
	printf("程序结束。\n");
	
	return 0;
}
char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');   // 查找换行符 
        if (find)                  // 如果地址不是NULL,
            *find = '\0';          // 在此处放置一个空字符 
        else
            while (getchar() != '\n')
                continue;          // 处理输入行中剩余的字符 
    }
    return ret_val;
}
void print(const struct message nu)
{
	if (nu.name.mname[0] == '\0')//中间名是空 
	{
		printf("%s,%s . -- %s\n", nu.name.fname, nu.name.lname, nu.sonum);
	}		 
	else
	{
		printf("%s,%s %c. -- %s\n", nu.name.fname, nu.name.lname, nu.name.mname[0], nu.sonum); 
	}
}

5.exercise5.c

编写一个程序满足下面的要求。
a. 外部定义一个有两个成员的结构模板name: 一个字符串储存名,一个字符串储存姓。
b. 外部定义一个有3个成员的结构模板student: 一个name类型的结构,一个grade数组储存3个浮点型分数,一个变量储存3个分数平均数。
c. 在main()函数中声明一个内含CSIZE (CSIZE = 4)个student类型结构的数组,并初始化这些结构的名字部分。用函数执行d、e、f和g中描述的任务。
d. 以交互的方式获取每个学生的成绩,提示用户输入学生的姓名和分数。把分数储存到grade数组相应的结构中。可以在main()函数或其他函数中用循环来完成。
e. 计算每个结构的平均分,并把计算后的值赋给合适的成员。
f. 打印每个结构的信息。
g. 打印班级的平均分,即所有结构的数值成员的平均值。

//exercise14.5
#include 
#define CSIZE 4
#define LEN 20 

struct name {
	char surname[LEN];
	char name[LEN];	
};

struct student {
	struct name na;
	float grade[3];
	float average; 
};

void get_grade(struct student stu[], int n);//获取每个学生的分数 
void get_average(struct student stu[], int n);//获取每个学生的平均分数 
void print_student(struct student stu[], int n);//打印每个学生的信息 
void print_classarg(struct student stu[], int n);//计算班级平均分并打印 
int main(void)
{
	struct student stu[CSIZE] = 
	{
		{"zhang", "san"}, 
		{"li", "si"},
		{"wang", "wu"},
		{"tian", "qi"}
	
	};//初始化结构数组 
	
	get_grade(stu, CSIZE);
	get_average(stu, CSIZE);
	print_student(stu, CSIZE);
	print_classarg(stu, CSIZE);
	
	return 0;
}
void get_grade(struct student stu[], int n)
{
	int i, j;
	
	for (i = 0; i < n; i++)
	{
		printf("请输入%s %s的三个分数:\n", stu[i].na.surname, stu[i].na.name);
		for (j = 0; j < 3; j++)
		{
			while (scanf("%f", &stu[i].grade[j]) != 1)
			{
				while (getchar() != '\n')
				{
					continue;
				}
				printf("输入下一个正确的分数:\n");
			}
		}
	}
}
void get_average(struct student stu[], int n)
{
	int i;
	
	for (i = 0; i < n; i++)
	{
		stu[i].average = (stu[i].grade[0] + stu[i].grade[1] + stu[i].grade[2]) / 3;
	}
}
void print_student(struct student stu[], int n)
{
	int i;
	
	printf("以下是所有学生的信息:\n");
	for (i = 0; i < n; i++)
	{
		printf("%s %s的三个分数:%.1f %.1f %.1f,平均分:%.1f\n", stu[i].na.surname, stu[i].na.name
		, stu[i].grade[0], stu[i].grade[1], stu[i].grade[2], stu[i].average);
	}
}
void print_classarg(struct student stu[], int n)
{
	int i;
	float total = 0.0; 
	float class_average;
	printf("班级的平均分数:");
	for (i = 0; i < n; i++)
	{
		total += stu[i].average;
	}
	class_average = total / n;
	printf("%.2f。\n", class_average);
}

输出示例:

请输入zhang san的三个分数:
56 23 25
请输入li si的三个分数:
23.5 62.5 23
请输入wang wu的三个分数:
98 85 69
请输入tian qi的三个分数:
98 97 96
以下是所有学生的信息:
zhang san的三个分数:56.0 23.0 25.0,平均分:34.7
li si的三个分数:23.5 62.5 23.0,平均分:36.3
wang wu的三个分数:98.0 85.0 69.0,平均分:84.0
tian qi的三个分数:98.0 97.0 96.0,平均分:97.0
班级的平均分数:63.00。

6.exercise6.c

一个文本文件中保存着一个垒球队的信息。每行数据都是这样排列:
4 Jessie Joybat 5 2 1 1
第1项是球员号,为方便起见,其范围是0~18。第2项是球员的名。第3项是球员的姓。名和姓都是一个单词。第4项是官方统计的球员上场次数。接着3项分别是击中数、走垒数和打点(RBI)。
文件可能包含多场比赛的数据,所以同一位球员可能有多行数据,而且同一位球员的多行数据之间可能有其他球员的数据。编写一个程序,把数据储存到一个结构数组中。该结构中的成员要分别表示球员的名、姓、上场次数、击中数、走垒数、打点和安打率(稍后计算)。可以使用球员号作为数组的索引。该程序要读到文件结尾,并统计每位球员的各项累计总和。
世界棒球统计与之相关。例如,一次走垒和触垒中的失误不计入上场次数,但是可能产生一个RBI。但是该程序要做的是像下面描述的一样读取和处理数据文件,不会关心数据的实际含义。
要实现这些功能,最简单的方法是把结构的内容都初始化为零,把文件中的数据读入临时变量中, 然后将其加入相应的结构中。程序读完文件后,应计算每位球员的安打率,并把计算结果储存到结构的相应成员中。计算安打率是用球员的累计击中数除以上场累计次数。这是一个浮点数计算。最后,程序结合整个球队的统计数据,一行显示一位球员的累计数据。

//exercise14.6
#include 
#include 
#include 
#define LEN 19
typedef struct {
	int id;
	char name[LEN];
	char surname[LEN];
	int time;
	int beat;
	int walk;
	int RBI;
	float rate;
} TEAM; //定义结构 
static TEAM players[LEN];//创建球员数组,最多不超过19个人 

int read_data(FILE * fp, TEAM players[], int n);//从文件中获取球员信息,并返回球员个数 
int get_rate(TEAM players[], int n);//获取每个球员的安打率 
void print(TEAM players[], int n);//打印球员信息
void print_pre(FILE * fp);//打印文件信息 
int main(void)
{
	FILE * fp;
	int line;
	
	if ((fp = fopen("exercise6.txt", "r")) == NULL)//打开文件失败就报错,读模式 
	{
		fprintf(stderr,"打开文件失败。\n");
		exit(EXIT_FAILURE); 
	}
	print_pre(fp);
	rewind(fp);//定位到文件开始处 
	line = read_data(fp, players, LEN);//调用函数执行相对应功能 
	get_rate(players, line);
	print(players, line);	
	if (fclose(fp) != 0)//关闭文件错误时则报错 
    {
        fprintf(stderr, "关闭exercise6.txt失败。\n");
    }
	return 0;
}
void print_pre(FILE * fp)
{
	int get_id, beat, time, walk, RBI;//获取每行对应的信息 
	char name[LEN];
	char surname[LEN];
	
	printf("以下是文件的数据:\n");
	while (fscanf(fp, "%d %18s %18s %d %d %d %d", &get_id, name, surname, &time, &beat, &walk, &RBI) == 7
		&& !feof(fp))
	{
		printf("%d %s %s %d %d %d %d\n", get_id, name, surname, time, beat, walk, RBI);//打印文件数据 
	}
}
int read_data(FILE * fp, TEAM players[], int n)
{
	int count = 0;//计数,用来获取文件一共储存了多少行数据 
	int get_id, beat, time, walk, RBI;//获取每行对应的信息 
	char name[LEN];
	char surname[LEN];
	int temp[LEN] = {-1}; //临时数组,存放球员的id 
	int total;//用于循环计数,数组下标 
	int flag = 1;//标记位,表示没有读取到已经存进了数组的id 
	int temp_count = 0;//返回值,保存有几个球员 

	while (fscanf(fp, "%d %18s %18s %d %d %d %d", &get_id, name, surname, &time, &beat, &walk, &RBI) == 7
		&& !feof(fp) && count < n) 
		//feof()当上一次输入调用检测到文件结尾时,feof()函数返回一个非零值
	{
		for (total = 0;total < count + 1; total++)//从0开始,一直到已经读取的行数为止 
		{
			if (temp[total] == get_id)//如果读取的id已经存进了数组 
			{
				players[total].time += time;//在对应的球员下,累计相应的上场次数等需要累加的值 
				players[total].beat += beat;
				players[total].walk += walk;
				players[total].RBI += RBI;
				flag = 0;//标记位赋值0,表示这行数据的球员已经存了 
				break;//跳出循环 
			}
		}
		if (flag == 1)//表示这是一个新球员 
		{
			players[temp_count].id = get_id;//给相应的球员结构赋值 
			strcpy(players[temp_count].name,name);
			strcpy(players[temp_count].surname,surname);
			players[temp_count].time = time;
			players[temp_count].beat = beat;
			players[temp_count].walk = walk;
			players[temp_count].RBI = RBI;
			temp[temp_count] = get_id;
			temp_count++;//新球员,数组存了一个,下一个数组元素下标 
		}
		flag = 1;//重新置为1,进行下一行数据读取 
		count++;//读取行数+1 
	} 
	return temp_count;//返回球员数 
}
int get_rate(TEAM players[], int n)
{
	int i;
	
	for (i = 0; i < n; i++)
	{
		players[i].rate = 1.0 *players[i].beat / players[i].time;
		//这样计算就不会舍入,会得到一个浮点数 
	}
}
void print(TEAM players[], int n)
{
	int i;
	
	if (n == 0)
	{
		printf("没有球员数据。\n");
	}
	else
	{
		printf("以下是球员数据:\n");
		for (i = 0; i < n; i++)
		{
			printf("%d %s %s %d %d %d %d %.2f\n",
			players[i].id, players[i].name, players[i].surname, players[i].time,
			players[i].beat, players[i].walk, players[i].RBI, players[i].rate);
		}
	}	
}

输出示例:

以下是文件的数据:
4 Jessie Joybat 5 2 1 1
3 Jee Jot 6 3 2 1
6 Zhang san 6 3 2 1
3 Jee Jot 7 1 2 1
1 Lily Neo 10 3 6 2
以下是球员数据:
4 Jessie Joybat 5 2 1 1 0.40
3 Jee Jot 13 4 4 2 0.31
6 Zhang san 6 3 2 1 0.50
1 Lily Neo 10 3 6 2 0.30

7.exercise7.c

修改程序booksave.c,从文件中读取每条记录并显示出来,允许用户删除记录或修改记录的内容。如果删除记录,把空出来的空间留给下一个要读入的记录。要修改现有的文件内容,必须用"r+b"模式,而不是"a+b"模式。而且,必须更加注意定位文件指针,防止新加入的记录覆盖现有记录。最简单的方法是改动储存在内存中的所有数据,然后再把最后的信息写入文件。跟踪的一个方法是在book结构中添加一个成员表示是否该项被删除。

//exercise14.7
#include 
#include 
#include 
#define MAXTITL  40
#define MAXAUTL  40
#define MAXBKS   10             /* 最大书籍数量 */
char * s_gets(char * st, int n);
struct book {                   /* 建立book模板 */
    char title[MAXTITL];
    char author[MAXAUTL];
    float value;
    int is_delete;//标记是否删除,是的话,值为1 
};
char get_char();//获取输入的信息 
void change(struct book * p);//更改书籍的信息 
int main(void)
{
    struct book library[MAXBKS]; //存放书籍 
    int count = 0;
    int index, filecount;
    FILE * pbooks;
    int size = sizeof (struct book);
    char ch;//获取输入的选项 
    struct book library_temp[MAXBKS]; //暂时存放 
    int i = 0;
    
    if ((pbooks = fopen("exercise7.txt", "r+")) == NULL)//r+模式 
    {
        fprintf(stderr, "打不开文件。\n");
        exit(EXIT_FAILURE);
    } 
    rewind(pbooks);            /* 定位到文件开始处 */
    while (count < MAXBKS &&  fread(&library[count], size, 1, pbooks) == 1)
    {//从文件中读取书 
        if (count == 0)
        {
        	puts("文件中已经有的:");//先逐个打印书籍信息 
		}
		library[count].is_delete = 0;    
        printf("%s by %s: $%.2f\n",library[count].title,library[count].author, library[count].value);
        
        puts("要修改或删除这条记录吗?(要输入y,不要就输入n)"); 
        ch = get_char();//获取y或n 
		if (ch == 'y')
		{
			puts("修改记录输入y,删除记录输入n:");
			ch = get_char();//继续利用这个函数 
			if (ch == 'y') 
			{
				change(&library[count]);//修改这本书的信息 
			}
			else
			{
				library[count].is_delete = 1;//这本书删除掉,标记置为1 
				printf("%s by %s在文件已经被删除。\n", library[count].title, library[count].author);
			}
		}
        count++;//读取下一本 
    }
    filecount = count;//文件中一共读取的书籍数量 
    if (count == MAXBKS)//等于最大数量 
    {
        fputs("exercise7.txt空间已经满了。", stderr);
        exit(EXIT_FAILURE);//退出 
    }  
    puts("输入新的书名(在一行开始处按回车退出):");
    while (count < MAXBKS && s_gets(library[count].title, MAXTITL) != NULL
           && library[count].title[0] != '\0')
    {
    	library[count].is_delete = 0;//先给书的删除标记赋值 
        puts("输入作者名:");
        s_gets(library[count].author, MAXAUTL);
        puts("输入书的价格:");
        scanf("%f", &library[count].value);
        count++;//书籍总数量+1,包括文件中已经存了的 
        while (getchar() != '\n')
            continue;                
        if (count < MAXBKS)
            puts("输入下一本书的书名:");
    }   
    if (count > 0)
    {
        puts("这是书的清单:");
        for (index = 0; index < count; index++)
        {//循环打印所有书 
        	if (library[index].is_delete == 0)//没删除的书籍才打印 
			{
				printf("%s by %s: $%.2f\n",library[index].title,library[index].author, library[index].value);
			} 
		}		
		for (index = 0; index < count; index++)//没删除的书籍存放在临时结构中 
        {
        	if (library[index].is_delete == 0)
			{
				 library_temp[i] = library[index];
				 i++;//存进临时结构就+1 
			} 
		}
		for (index = 0; index < i; index++)
        {
			library[index] = library_temp[index];
			//再把临时结构的存在静态结构中,下次运行程序读取书籍 
		}
		if (fclose(pbooks) != 0)//把文件关了 
		{
			fprintf(stderr, "关闭文件失败。\n");
        	exit(EXIT_FAILURE);
		}
		if ((pbooks = fopen("exercise7.txt", "w")) == NULL)//再以写模式打开文件 
	    {
	        fprintf(stderr, "打开文件失败。\n");
	        exit(EXIT_FAILURE);
	    }
		for (index = 0; index < i; index++)
        {//剩余书籍存进文件中 
			fwrite(&library[index], size, 1, pbooks);//把没有删除的书逐个写入文件 
		}            
    }
    else
    {
    	puts("没有书。\n");
	} 	    
    if (fclose(pbooks) != 0)
	{
		fprintf(stderr, "关闭文件失败。\n");
        exit(EXIT_FAILURE);
	}
    puts("程序结束。");
    
    return 0;
}
char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');   
        if (find)                  
            *find = '\0';          
        else
            while (getchar() != '\n')
                continue;          
    }
    return ret_val;
}
char get_char()
{
	char temp[10];//输入暂存在这个数组 
	
	while (s_gets(temp, 10) == NULL || (temp[0] != 'y' && temp[0] != 'n') || temp[1] != '\0')
	{//输入为空或输入的第一个字符不为y、n中的一个或第二个字符不是换行符 
        puts("请输入y或n。");  
	}
	return temp[0];//返回y或n 
}
void change(struct book * p)
{
	puts("输入修改后的书名(在一行开始处按回车退出):");
	s_gets(p->title, MAXAUTL);
 	puts("输入修改后的作者名:");
    s_gets(p->author, MAXAUTL);
    puts("输入修改后的书的价格:");
    scanf("%f", &p->value);
    while (getchar() != '\n')
    {
    	continue;
	}
}

第一次运行程序,添加两本书:

输入新的书名(在一行开始处按回车退出):
c
输入作者名:
zhangsan
输入书的价格:
11
输入下一本书的书名:
c++
输入作者名:
lisi
输入书的价格:
22
输入下一本书的书名:

这是书的清单:
c by zhangsan: $11.00
c++ by lisi: $22.00
程序结束。

第二次运行程序,再添加三本书:

文件中已经有的:
c by zhangsan: $11.00
要修改或删除这条记录吗?(要输入y,不要就输入n)
n
c++ by lisi: $22.00
要修改或删除这条记录吗?(要输入y,不要就输入n)
n
输入新的书名(在一行开始处按回车退出):
java
输入作者名:
wangwu
输入书的价格:
33.5
输入下一本书的书名:
python
输入作者名:
maliu
输入书的价格:
44.5
输入下一本书的书名:
c#
输入作者名:
tianqi
输入书的价格:
66.2
输入下一本书的书名:

这是书的清单:
c by zhangsan: $11.00
c++ by lisi: $22.00
java by wangwu: $33.50
python by maliu: $44.50
c# by tianqi: $66.20

第三次运行程序,修改一本书:

文件中已经有的:
c by zhangsan: $11.00
要修改或删除这条记录吗?(要输入y,不要就输入n)
y
修改记录输入y,删除记录输入n:
y
输入修改后的书名(在一行开始处按回车退出):
c++++
输入修改后的作者名:
zhaojiu
输入修改后的书的价格:
77
c++ by lisi: $22.00
要修改或删除这条记录吗?(要输入y,不要就输入n)
n
java by wangwu: $33.50
要修改或删除这条记录吗?(要输入y,不要就输入n)
n
python by maliu: $44.50
要修改或删除这条记录吗?(要输入y,不要就输入n)
n
c# by tianqi: $66.20
要修改或删除这条记录吗?(要输入y,不要就输入n)
n
输入新的书名(在一行开始处按回车退出):

这是书的清单:
c++++ by zhaojiu: $77.00
c++ by lisi: $22.00
java by wangwu: $33.50
python by maliu: $44.50
c# by tianqi: $66.20
程序结束。

删除两本书:

文件中已经有的:
c++++ by zhaojiu: $77.00
要修改或删除这条记录吗?(要输入y,不要就输入n)
n
c++ by lisi: $22.00
要修改或删除这条记录吗?(要输入y,不要就输入n)
y
修改记录输入y,删除记录输入n:
n
c++ by lisi在文件已经被删除。
java by wangwu: $33.50
要修改或删除这条记录吗?(要输入y,不要就输入n)
n
python by maliu: $44.50
要修改或删除这条记录吗?(要输入y,不要就输入n)
y
修改记录输入y,删除记录输入n:
n
python by maliu在文件已经被删除。
c# by tianqi: $66.20
要修改或删除这条记录吗?(要输入y,不要就输入n)
n
输入新的书名(在一行开始处按回车退出):

这是书的清单:
c++++ by zhaojiu: $77.00
java by wangwu: $33.50
c# by tianqi: $66.20
程序结束。

再运行看看:

文件中已经有的:
c++++ by zhaojiu: $77.00
要修改或删除这条记录吗?(要输入y,不要就输入n)
n
java by wangwu: $33.50
要修改或删除这条记录吗?(要输入y,不要就输入n)
n
c# by tianqi: $66.20
要修改或删除这条记录吗?(要输入y,不要就输入n)
n
输入新的书名(在一行开始处按回车退出):

这是书的清单:
c++++ by zhaojiu: $77.00
java by wangwu: $33.50
c# by tianqi: $66.20
程序结束。

输入验证没有做的完善;一本书一本书提示用户是否需要修改或者删除其实是不好的,应该打印所有书籍,然后输入书籍的名称去进行匹配,再进行修改删除,这样的话更合理,但是代码量加大很多,留待后续。

创建一个临时结构去存放剩下的书籍其实也比较低效,应该使用链表,在删除一本书后,把前一本书的指针指向删除的那本书的后面的书,这属于数据结构内容,留待以后。

8.exercise8.c

巨人航空公司的机群由12个座位的飞机组成。它每天飞行一个航班。根据下面的要求,编写一个座位预订程序。
a.该程序使用一个内含12个结构的数组。每个结构中包括:一个成员表示座位编号、一个成员表示座位是否已被预订、一个成员表示预订人的名、一个成员表示预订人的姓。
b.该程序显示下面的菜单:
To choose a function, enter its letter label: 要选择功能,请输入其字母标签:
a)Show number of empty seats a)显示空座位的数量
b)Show list of empty seats b)显示空座位列表
c)Show alphabetical list of seats c)按字母顺序显示座位列表
d) Assign a customer to a seat assignment d)为客户分配座位
e) Delete a seat assignment e)删除一个已经分配的座位
f) Quit f)退出

c.该程序能成功执行上面给出的菜单。选择d)和e)要提示用户进行额外输入,每个选项都能让用户中止输入。
d.执行特定程序后,该程序再次显示菜单,除非用户选择f)。

//exercise14.8
#include 
#include 
#include 
#define SEAT 12
#define LEN 20
struct planes {               
    int id;
    int is_book;
    char fname[LEN];
    char lname[LEN];
};
void menu();//菜单显示函数 
char get_char();//获取输入的菜单选项 
char * s_gets(char * st, int n);
int a_num(const struct planes plane[], int n);
void b_list(const struct planes plane[], int n);
void c_show(const struct planes plane[], int n);
void d_assign(struct planes *plane, int n);
void no_assign(const struct planes plane[], int n, int *arr); 
int get_id(int *arr, int n);
void e_delete(struct planes *plane, int n);
void is_assign(const struct planes plane[], int n, int *arr); 
int main(void)
{
    struct planes plane[SEAT];
    char choice;
    FILE *fp;
    int size = sizeof(struct planes);
    int i;
    int empty;
    
    if ((fp = fopen("exercise8.txt", "r+")) == NULL)//读写模式打开 
    {
    	fprintf(stderr,"打开文件exercise8.txt失败\n");
    	exit(EXIT_FAILURE);
	}
	if (fgetc(fp) == EOF) 
	{/*先读一下文件,如果文件为空,也就是第一次运行程序时
	 int fgetc(FILE *stream)从指定的流 stream 获取下一个字符(一个无符号字符)
	 ,并把位置标识符往前移动,如果到达文件末尾或发生读错误,则返回 EOF*/
		for (i = 0; i < SEAT; i++)//先给12个座位编号1-12,然后都是未预定 
		{
			plane[i].id = i + 1; 
			plane[i].is_book = 0;//座位没被预定 
		}
		fwrite(plane, size, SEAT, fp);//把这12个座位编号并且都写进文件 
	}//就只有第一次运行程序时会执行这一段,后面文件就有内容了
	rewind(fp);//回到文件开始 
	fread(plane, size, SEAT, fp);//把文件内容读入结构数组中 
	if (ferror(fp) != 0)
	{
		fprintf(stderr, "写入文件exercise8.txt失败,\n");
		exit(EXIT_FAILURE);
	}
	
    menu();//显示菜单 
    choice = get_char();//获取选项 
    while (choice != 'f')//输入的不是f,就一直循环 
    {
    	switch (choice)
    	{
    		case 'a':
    				printf("一共有%d个空座位。\n", a_num(plane, SEAT));
    				break;
    		case 'b':
    				b_list(plane, SEAT);
    				break;
    		case 'c':
    				c_show(plane, SEAT);
    				break;
    		case 'd':
    				d_assign(plane, SEAT);
    				break;
    		case 'e':
    				e_delete(plane, SEAT);
    				break;
    		default:
    				printf("程序出错了。\n");
    				break;
		}
		menu();
		choice = get_char();
	}
	if (fclose(fp) != 0)//关闭文件 
	{
		fprintf(stderr, "关闭文件exercise8.txt失败,\n");
		exit(EXIT_FAILURE);
	}
	if ((fp = fopen("exercise8.txt", "w")) == NULL)//写模式打开 
    {
    	fprintf(stderr,"打开文件exercise8.txt失败\n");
    	exit(EXIT_FAILURE);
	}
	fwrite(plane, size, SEAT, fp);
	//把做了更改后的座位信息又重新写回该文件,下次打开程序会保留上次程序运行后的结果 
	if (ferror(fp) != 0)
	{
		fprintf(stderr, "写入文件exercise8.txt失败,\n");
		exit(EXIT_FAILURE);
	}
	if (fclose(fp) != 0)
	{
		fprintf(stderr, "关闭文件exercise8.txt失败,\n");
		exit(EXIT_FAILURE);
	}
    puts("程序结束。");
    
    return 0;
}
void menu()
{
	printf("要选择功能,请输入相应字母:\n");
	printf("a)显示空座位的数量\n");
	printf("b)显示空座位列表\n");
	printf("c)按字母顺序显示座位列表\n");
	printf("d)为客户分配座位\n");
	printf("e)删除一个已经分配的座位\n");
	printf("f)退出\n");
}
char get_char()
{
	char temp[3];//输入暂存在这个数组 
	
	while (s_gets(temp, 3) == NULL || temp[1] != '\0' || temp[0] > 'f' || temp[0] < 'a')
	{//输入的不是单个字母,或不是在a-f之间的字母,就循环输入 
		printf("请输入正确的字母:");
	}
	return temp[0];//返回输入的选项 
}
char * s_gets(char * st, int n)//输入字符串 
{
    char * ret_val;
    char * find;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');   
        if (find)                  
            *find = '\0';          
        else
            while (getchar() != '\n')
                continue;          
    }
    return ret_val;
}
int a_num(const struct planes plane[], int n)//a功能, 显示有几个空座位 
{
	int i;
	int count = 0;//空座位计数 
	
	for (i = 0; i < n; i++)
	{
		if (plane[i].is_book == 0)//座位的预定标记是0,则是空座位 
		{
			count++; 
		}
	}	
	return count;
}
void b_list(const struct planes plane[], int n)//b功能,显示未被预定的座位编号 
{
	int i;
	int count;//空座位计数 
	
	count = a_num(plane, n);
	if (count == 0)
	{
		printf("没有空座位。\n");
	}
	else
	{
		printf("%d个空座位的编号:", count);
		for (i = 0; i < n; i++)
		{
			if (plane[i].is_book == 0)//座位是空座位 
			{
				printf("%d ", plane[i].id);
			}
		}
		printf("\n");
	}	
}
void c_show(const struct planes plane[], int n)//c功能,以名字的顺序排序展示 
{
	struct planes temp;//临时结构
	struct planes temp_plane[n];//临时结构数组 
	int i, j;
	int top;
	int count = 0;
	
	if (a_num(plane, n) == SEAT)
	{
		printf("全是空座位。\n");
	}
	else
	{
		for (i = 0; i < n; i++)//赋值 
		{
			if (plane[i].is_book == 1)//是预定的座位 
			{
				temp_plane[count] = plane[i];//存入临时结构数组 
				count++;//临时结构数组存放座位+1 
			}
		}
		for (i = 0; i < count; i++)//临时结构数组冒泡排序 
		{
			for (top = i + 1; top < count; top++)//内层循环 
			{
				if (strcmp(temp_plane[i].fname, temp_plane[top].fname) > 0)
				//比较两个座位预定人的名的顺序 
				{
					temp = temp_plane[i];//就是交换顺序 
					temp_plane[i] = temp_plane[top];
					temp_plane[top] =temp;
				}
			}			
		}
		printf("以下是按字母顺序排列的座位列表:\n");
		for (i = 0; i < count; i++)
		{	
			printf("%d %d %s %s\n",
				temp_plane[i].id, temp_plane[i].is_book, temp_plane[i].fname, temp_plane[i].lname);
		}
	}	
}
void d_assign(struct planes *plane, int n)//d功能,预定一个座位 
{	
	int arr[n];//存放空座位 
	int getid;//获取想预定的座位编号 
	int i;
	
	if (a_num(plane, n) == 0)
	{
		printf("没有空座位。\n");
	}
	else
	{
		no_assign(plane, SEAT, arr);//调用此函数,把存放空座位的数组存储空座位的编号	
		printf("请输入你要选择的座位编号:");
		getid = get_id(arr, n);//获取想预定的座位编号 
		while (getid == 0)//此作为不是空座位,重新选一个座位 
		{
			printf("请输入没被预定的座位号:");
			getid = get_id(arr, n);
		}
		printf("请输入您的名:");//填写相关信息 
		s_gets(plane[getid - 1].fname, n);
		printf("请输入您的姓:");
		s_gets(plane[getid - 1].lname, n);
		plane[getid - 1].is_book = 1;//标记这个座位成功被预定了 
	} 
}
void no_assign(const struct planes plane[], int n, int *arr)//没被预定的座位 
{	
	int i;
	
	for (i = 0; i < n; i++)
	{
		if (plane[i].is_book == 0)//座位没被预定 
		{
			arr[i] = plane[i].id;//存在空座位数组中 
		}
	}	
}
int get_id(int *arr, int n)//获取输入的编号 
{
	int input;
	int i; 

	while (scanf("%d", &input) != 1 || getchar() != '\n' || input > 12 || input < 1)
	{//如果输入的不是数字或者数字不在1-12中 ,循环输入 
		printf("请输入正确的数字:", input);
		while (getchar() != '\n')
		{
			continue;
		}
	}
	for (i = 0; i < n; i++)
	{
		if (input == arr[i])//输入的值和数组中的值逐个比较,满足则说明输入有效 
		{
			return input;//返回这个有效值	
		}
	}
	return 0;
}
void e_delete(struct planes *plane, int n)//e功能,删除一个座位预订信息 
{	
	int arr[n];//被预定的座位数组 
	int getid;
	int i;
	
	if (a_num(plane, n) == SEAT)
	{
		printf("全是空座位。\n");
		return; 
	}
	else
	{
		is_assign(plane, SEAT, arr);	
		printf("请输入你要删除的座位编号:");
		getid = get_id(arr, n);
		while (getid == 0)
		{
			printf("请输入已经预定的座位号:");
			getid = get_id(arr, n);
		}
		strcpy(plane[getid - 1].fname, "");//删除座位信息 
		strcpy(plane[getid - 1].lname, "");
		plane[getid - 1].is_book = 0;//座位标记为空座位,删除座位成功 
	} 
}
void is_assign(const struct planes plane[], int n, int *arr)//已经被预定的座位 
{
	
	int i;
	
	for (i = 0; i < n; i++)
	{
		if (plane[i].is_book == 1)//座位被预定 
		{
			arr[i] = plane[i].id;//存在预定数组中 
		}
	}	
}

第一次运行程序,空的exercise8.txt文件会填充12个座位信息,展示b功能:

要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)退出
b
9个空座位的编号:1 3 4 6 7 9 10 11 12
要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)退出
f
程序结束。

第二次运行程序,预定三个座位,展示d功能:

要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)退出
b
12个空座位的编号:1 2 3 4 5 6 7 8 9 10 11 12
要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)退出
d
请输入你要选择的座位编号:2
请输入您的名:san
请输入您的姓:zhang
要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)退出
d
请输入你要选择的座位编号:5
请输入您的名:si
请输入您的姓:li
要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)退出
d
请输入你要选择的座位编号:8
请输入您的名:liu
请输入您的姓:ma
要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)退出
f
程序结束。

第三次运行程序,展示a、c、e功能:

要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)退出
a
一共有9个空座位。
要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)退出
c
以下是按字母顺序排列的座位列表:
8 1 liu ma
2 1 san zhang
5 1 si li
要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)退出
e
请输入你要删除的座位编号:2
要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)退出
c
以下是按字母顺序排列的座位列表:
8 1 liu ma
5 1 si li
要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)退出
f
程序结束。

可以用fgetc()函数判断文件是否为空,然后填充空文件;需要修改结构数组本身的内容时,要传入指针,否则传入结构数组即可,而且要加const限定符,以免内容被篡改;输入验证做的还不是很到位,因为在使用d功能时,就得把修改给做完。无法中途终止这个功能并回到菜单,应该提示q就终止这个功能,这必须要用到判定输入内容的函数,暂时没找到解决方法。

9.exercise9.c

巨人航空公司(编程练习8)需要另一架飞机(容量相同),每天飞4班(航班102、311、444和519)。把程序扩展为可以处理4个航班。用一个顶层菜单提供航班选择和退出。选择一个特定航班,就会出现和编程练习8类似的菜单。但是该菜单要添加一个新选项:确认座位分配。而且,菜单中的退出是返回顶层菜单。每次显示都要指明当前正在处理的航班号。另外,座位分配显示要指明确认状态。

//exercise14.9
#include 
#include 
#include 
#define LEN 20
#define SEAT 12

struct planes {               
    int id;
    int is_book;
    char fname[LEN];
    char lname[LEN];
};

char get_char();//获取输入的菜单选项
char * s_gets(char * st, int n);//输入字符串
void airlane_menu();//进入航班后,选择相应功能 
void plane_deal(char choice);//进入相应的航班处理程序
void plane_menu();//响应航班菜单显示函数
int a_num(const struct planes plane[], int n);//a功能,显示有几个空座位
void b_list(const struct planes plane[], int n);//b功能,显示未被预定的座位编号
void c_show(const struct planes plane[], int n);//c功能,以名字的顺序排序展示
void d_assign(struct planes *plane, int n);//d功能,预定一个座位
void no_assign(const struct planes plane[], int n, int *arr); //没被预定的座位
int get_id(int *arr, int n);//获取输入的编号
void e_delete(struct planes *plane, int n);//e功能,删除一个座位预订信息
void is_assign(const struct planes plane[], int n, int *arr);//已经被预定的座位
void f_confirm(const struct planes plane[], int n); //f功能,显示已分配的座位座位
int main(void)
{
	char choice;

	airlane_menu();
    choice = get_char();//获取选项 
    while (choice != 'e')
    {
    	if (choice < 'a' || choice > 'e')
		{
			printf("请输入正确的字母:");
			choice = get_char();
			break; 
		}
		plane_deal(choice);//进入对应航班 
		airlane_menu(); 
		choice = get_char();//获取选项
	}
	
	puts("程序结束。");
	return 0; 
}
char get_char()
{
	char temp[3];//输入暂存在这个数组 
	
	while (s_gets(temp, 3) == NULL || temp[1] != '\0' || temp[0] > 'z' || temp[0] < 'a')
	{//输入的不是单个字母,或不是在a-f之间的字母,就循环输入 
		printf("请输入正确的字母:");
	}
	return temp[0];//返回输入的选项 
}
char * s_gets(char * st, int n)//输入字符串 
{
    char * ret_val;
    char * find;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');   
        if (find)                  
            *find = '\0';          
        else
            while (getchar() != '\n')
                continue;          
    }
    return ret_val;
}
void airlane_menu()
{
	puts("a)102    b)311    c)444    d)519    e)退出程序");
	printf("要选择航班,请输入相应字母:");
}
void plane_deal(char choice)
{
	char filename[LEN];//文件名,存储对应航班 
	FILE *fp;
	struct planes plane[SEAT];
    int size = sizeof(struct planes);
    int i;
    int empty;
    char choice_two;
    int num;//存储航班号 
	 
	switch (choice)
    {
  		case 'a':
    			puts("以下是102航班:");
    			num = 102;//120航班 
    			strcpy(filename, "exercise9a.txt");//102航班信息存储在对应文件中 
    			break;
    	case 'b':
    			puts("以下是311航班:");
    			num = 311;
    			strcpy(filename, "exercise9b.txt");
    			break;
    	case 'c':
    			puts("以下是444航班:");
    			num = 444;
    			strcpy(filename, "exercise9c.txt");
    			break;
    	case 'd':
    			puts("以下是519航班:");
    			num = 519;
    			strcpy(filename, "exercise9d.txt");
    			break;
    		default:
    			printf("程序出错了。\n");
    			break;
	}
	if ((fp = fopen(filename, "r+")) == NULL)//读写模式打开对应文件 
    {
    	fprintf(stderr,"打开文件%s.失败\n", filename); 
    	exit(EXIT_FAILURE);
	}
	if (fgetc(fp) == EOF) 
	{/*先读一下文件,如果文件为空,也就是第一次运行程序时
	 int fgetc(FILE *stream)从指定的流 stream 获取下一个字符(一个无符号字符)
	 ,并把位置标识符往前移动,如果到达文件末尾或发生读错误,则返回 EOF*/
		for (i = 0; i < SEAT; i++)//先给12个座位编号1-12,然后都是未预定 
		{
			plane[i].id = i + 1; 
			plane[i].is_book = 0;//座位没被预定 
		}
		fwrite(plane, size, SEAT, fp);//把这12个座位编号并且都写进文件 
	}//就只有第一次运行程序时会执行这一段,后面文件就有内容了
	rewind(fp);//回到文件开始 
	fread(plane, size, SEAT, fp);//把文件内容读入结构数组中 
	if (ferror(fp) != 0)
	{
		fprintf(stderr, "写入文件%s失败。\n", filename);
		exit(EXIT_FAILURE);
	}
	plane_menu();//打开功能菜单 
	choice_two = get_char();//获取选项 
    while (choice_two != 'g')//输入的不是g,就一直在本航班内循环 
    {
    	if (choice_two < 'a' || choice_two > 'g')
		{
			printf("请输入正确的字母:");
			choice_two = get_char();
			break; 
		}
    	switch (choice_two)
    	{
    		case 'a':
    				printf("一共有%d个空座位。\n", a_num(plane, SEAT));
    				break;
    		case 'b':
    				b_list(plane, SEAT);
    				break;
    		case 'c':
    				c_show(plane, SEAT);
    				break;
    		case 'd':
    				d_assign(plane, SEAT);
    				break;
    		case 'e':
    				e_delete(plane, SEAT);
    				break;
			case 'f':
    				f_confirm(plane, SEAT);
    				break;
    		default:
    				printf("程序出错了。\n");
    				break;
		}
		printf("以下是%d航班:", num);//提示用户还在此航班中操作 
		plane_menu();
		choice_two = get_char();
	}
	if (fclose(fp) != 0)//关闭对应航班的文件 
	{
		fprintf(stderr, "关闭文件%s失败,\n", filename);
		exit(EXIT_FAILURE);
	}
	if ((fp = fopen(filename, "w")) == NULL)//写模式打开 
    {
    	fprintf(stderr,"打开文件%s失败\n", filename);
    	exit(EXIT_FAILURE);
	}
	fwrite(plane, size, SEAT, fp);
	//把做了更改后的座位信息又重新写回该文件,下次打开程序会保留上次程序运行后的结果 
	if (ferror(fp) != 0)
	{
		fprintf(stderr, "写入文件%s失败,\n", filename);
		exit(EXIT_FAILURE);
	}
	if (fclose(fp) != 0)
	{
		fprintf(stderr, "关闭文件%s失败,\n", filename);
		exit(EXIT_FAILURE);
	}
}
void plane_menu()
{
	printf("要选择功能,请输入相应字母:\n");
	printf("a)显示空座位的数量\n");
	printf("b)显示空座位列表\n");
	printf("c)按字母顺序显示座位列表\n");
	printf("d)为客户分配座位\n");
	printf("e)删除一个已经分配的座位\n");
	printf("f)确认分配座位\n");
	printf("g)退出\n");
}
int a_num(const struct planes plane[], int n)//a功能, 显示有几个空座位 
{
	int i;
	int count = 0;//空座位计数 
	
	for (i = 0; i < n; i++)
	{
		if (plane[i].is_book == 0)//座位的预定标记是0,则是空座位 
		{
			count++; 
		}
	}	
	return count;
}
void b_list(const struct planes plane[], int n)//b功能,显示未被预定的座位编号 
{
	int i;
	int count;//空座位计数 
	
	count = a_num(plane, n);
	if (count == 0)
	{
		printf("没有空座位。\n");
	}
	else
	{
		printf("%d个空座位的编号:", count);
		for (i = 0; i < n; i++)
		{
			if (plane[i].is_book == 0)//座位是空座位 
			{
				printf("%d ", plane[i].id);
			}
		}
		printf("\n");
	}	
}
void c_show(const struct planes plane[], int n)//c功能,以名字的顺序排序展示 
{
	struct planes temp;//临时结构
	struct planes temp_plane[n];//临时结构数组 
	int i, j;
	int top;
	int count = 0;
	
	if (a_num(plane, n) == SEAT)
	{
		printf("全是空座位。\n");
	}
	else
	{
		for (i = 0; i < n; i++)//赋值 
		{
			if (plane[i].is_book == 1)//是预定的座位 
			{
				temp_plane[count] = plane[i];//存入临时结构数组 
				count++;//临时结构数组存放座位+1 
			}
		}
		for (i = 0; i < count; i++)//临时结构数组冒泡排序 
		{
			for (top = i + 1; top < count; top++)//内层循环 
			{
				if (strcmp(temp_plane[i].fname, temp_plane[top].fname) > 0)
				//比较两个座位预定人的名的顺序 
				{
					temp = temp_plane[i];//就是交换顺序 
					temp_plane[i] = temp_plane[top];
					temp_plane[top] =temp;
				}
			}			
		}
		printf("以下是按字母顺序排列的座位列表:\n");
		for (i = 0; i < count; i++)
		{	
			printf("%d %d %s %s\n",
				temp_plane[i].id, temp_plane[i].is_book, temp_plane[i].fname, temp_plane[i].lname);
		}
	}	
}
void d_assign(struct planes *plane, int n)//d功能,预定一个座位 
{	
	int arr[n];//存放空座位 
	int getid;//获取想预定的座位编号 
	int i;
	
	if (a_num(plane, n) == 0)
	{
		printf("没有空座位。\n");
	}
	else
	{
		no_assign(plane, SEAT, arr);//调用此函数,把存放空座位的数组存储空座位的编号	
		printf("请输入你要选择的座位编号:");
		getid = get_id(arr, n);//获取想预定的座位编号 
		while (getid == 0)//此作为不是空座位,重新选一个座位 
		{
			printf("请输入没被预定的座位号:");
			getid = get_id(arr, n);
		}
		printf("请输入您的名:");//填写相关信息 
		s_gets(plane[getid - 1].fname, n);
		printf("请输入您的姓:");
		s_gets(plane[getid - 1].lname, n);
		plane[getid - 1].is_book = 1;//标记这个座位成功被预定了 
	} 
}
void no_assign(const struct planes plane[], int n, int *arr)//没被预定的座位 
{	
	int i;
	
	for (i = 0; i < n; i++)
	{
		if (plane[i].is_book == 0)//座位没被预定 
		{
			arr[i] = plane[i].id;//存在空座位数组中 
		}
	}	
}
int get_id(int *arr, int n)//获取输入的编号 
{
	int input;
	int i; 

	while (scanf("%d", &input) != 1 || getchar() != '\n' || input > 12 || input < 1)
	{//如果输入的不是数字或者数字不在1-12中 ,循环输入 
		printf("请输入正确的数字:", input);
		while (getchar() != '\n')
		{
			continue;
		}
	}
	for (i = 0; i < n; i++)
	{
		if (input == arr[i])//输入的值和数组中的值逐个比较,满足则说明输入有效 
		{
			return input;//返回这个有效值	
		}
	}
	return 0;
}
void e_delete(struct planes *plane, int n)//e功能,删除一个座位预订信息 
{	
	int arr[n];//被预定的座位数组 
	int getid;
	int i;
	
	if (a_num(plane, n) == SEAT)
	{
		printf("全是空座位。\n");
		return; 
	}
	else
	{
		is_assign(plane, SEAT, arr);	
		printf("请输入你要删除的座位编号:");
		getid = get_id(arr, n);
		while (getid == 0)
		{
			printf("请输入已经预定的座位号:");
			getid = get_id(arr, n);
		}
		strcpy(plane[getid - 1].fname, "");//删除座位信息 
		strcpy(plane[getid - 1].lname, "");
		plane[getid - 1].is_book = 0;//座位标记为空座位,删除座位成功 
	} 
}
void is_assign(const struct planes plane[], int n, int *arr)//已经被预定的座位 
{
	
	int i;
	
	for (i = 0; i < n; i++)
	{
		if (plane[i].is_book == 1)//座位被预定 
		{
			arr[i] = plane[i].id;//存在预定数组中 
		}
	}	
}
void f_confirm(const struct planes plane[], int n)//f功能,显示已分配的座位座位
{
	int i;
	
	puts("已分配的座位:");
	for (i = 0; i < n; i++)
	{
		if (plane[i].is_book == 1)//座位的预定标记是0,则是已分配座位 
		{
			 printf("%d %d %s %s\n",
				plane[i].id, plane[i].is_book, plane[i].fname, plane[i].lname);
		}
	} 
}

在编程练习8的基础上进行修改,把4个航班的信息放在4个不同的文件中,分别是exercise9a.txt、exercise9b.txt、exercise9c.txt、exercise9d.txt第一次运行:

a)102    b)311    c)444    d)519    e)退出程序
要选择航班,请输入相应字母:a
以下是102航班:
要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)确认分配座位
g)退出
f
全是空座位。
以下是102航班:要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)确认分配座位
g)退出
d
请输入你要选择的座位编号:5
请输入您的名:san
请输入您的姓:zhang
以下是102航班:要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)确认分配座位
g)退出
g
a)102    b)311    c)444    d)519    e)退出程序
要选择航班,请输入相应字母:b
以下是311航班:
要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)确认分配座位
g)退出
d
请输入你要选择的座位编号:5
请输入您的名:si
请输入您的姓:li
以下是311航班:要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)确认分配座位
g)退出
d
请输入你要选择的座位编号:9
请输入您的名:liu
请输入您的姓:ma
以下是311航班:要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)确认分配座位
g)退出
c
以下是按字母顺序排列的座位列表:
9 1 liu ma
5 1 si li
以下是311航班:要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)确认分配座位
g)退出
g
a)102    b)311    c)444    d)519    e)退出程序
要选择航班,请输入相应字母:f
请输入正确的字母:e
程序结束。

第二次运行,还能读取上次运行后的数据:

a)102    b)311    c)444    d)519    e)退出程序
要选择航班,请输入相应字母:b
以下是311航班:
要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)确认分配座位
g)退出
f
已分配的座位:
5 1 si li
9 1 liu ma
以下是311航班:要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)确认分配座位
g)退出
e
请输入你要删除的座位编号:5
以下是311航班:要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)确认分配座位
g)退出
f
已分配的座位:
9 1 liu ma
以下是311航班:要选择功能,请输入相应字母:
a)显示空座位的数量
b)显示空座位列表
c)按字母顺序显示座位列表
d)为客户分配座位
e)删除一个已经分配的座位
f)确认分配座位
g)退出
g
a)102    b)311    c)444    d)519    e)退出程序
要选择航班,请输入相应字母:e
程序结束。

10.exercise10.c

编写一个程序,通过一个函数指针数组实现菜单。例如,选择菜单中的a,将激活由该数组第1个元素指向的函数。

//exercise14.10
#include 
#include 
#define LEN 2

void show(void (*pf)(int, int), int x, int y);
void a_f(int x, int y);
void b_f(int x, int y);
void menu(void);
char get_char(void);
char * s_gets(char * st, int n); 
int main(void)
{
	char ch;
	void (*pf[LEN])(int, int);//函数指针数组 
	int x, y;
	
	menu();
	puts("请输入两个整数:"); 
	while (scanf("%d %d", &x, &y) != 2)
	{
		while (getchar() != '\n')
		{
			continue;
		}
		puts("请输入两个整数:");
	} 
	while (getchar() != '\n')
	{
		continue;
	}
	puts("请输入相应的选项选择功能。");
	ch = get_char();
	while (ch != 'c')
	{
		switch(ch)
		{
			case 'a':
					pf[0] = a_f;//指针赋值 
					show(pf[0], x, y);//调用函数 
					break;
			case 'b':
					pf[1] = b_f;
					show(pf[1], x, y);
					break;
			default:
					printf("程序错误。\n");
					break;
		}
		puts("请输入相应的选项选择功能。");
		ch = get_char();
	}
    puts("请输入相应的选项选择功能。");
	return 0;
} 
void menu(void)
{
	puts("请输入相应的选项选择功能。");
	puts("a)计算两个整数的和");
	puts("b)计算两个整数的差");
	puts("c)退出");
}
void a_f(int x, int y)
{
	printf("%d + %d = %d\n", x, y, x + y);
}
void b_f(int x, int y)
{
	printf("%d - %d = %d\n", x, y, x - y);
}
char get_char(void)
{
	char temp[3];//输入暂存在这个数组 
	
	while (s_gets(temp, 3) == NULL || temp[1] != '\0' || temp[0] > 'c' || temp[0] < 'a')
	{//输入的不是单个字母,或不是在a-f之间的字母,就循环输入 
		printf("请输入正确的字母:");
	}
	return temp[0];//返回输入的选项 
}
char * s_gets(char * st, int n)//输入字符串 
{
    char * ret_val;
    char * find;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');   
        if (find)                  
            *find = '\0';          
        else
            while (getchar() != '\n')
                continue;          
    }
    return ret_val;
}
void show(void (*pf)(int, int), int x, int y)
{
	(*pf)(x, y);//使用函数指针调用函数 
}

输出示例:

请输入相应的选项选择功能。
a)计算两个整数的和
b)计算两个整数的差
c)退出
请输入两个整数:
5 6
请输入相应的选项选择功能。
a
5 + 6 = 11
请输入相应的选项选择功能。
b
5 - 6 = -1
请输入相应的选项选择功能。
c
程序结束。

简单示范,注意声明函数指针的时候,各种参数要对应上。

11.exercise11.c

编写一个名为transform()的函数,接受4个参数:内含double类型数据的源数组名、内含double类型数据的目标数组名、一个表示数组元素个数的int类型参数、函数名(或等价的函数指针)。transform()函数应把指定函数应用于源数组中的每个元素,并把返回值储存在目标数组中。例如:
transform(source, target, 100, sin);
该声明会把target[0]设置为sin(source[0]),等等,共有100个元素。在一个程序中调用transform()4次,以测试该函数。分别使用math.h函数库中的两个函数以及自定义的两个函数作为参数。

//exercise14.11
#include 
#include 
#define LEN 10
void transform(double *source, double *target, int n, double (*p)(double));
double add(double x);//加1 
double subtract(double x);//减1 
void show(const double arr[], int n);
int main(void)
{	
	double source[LEN] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0 ,8.0, 9.0, 10.0};
	double target[LEN] = {0.0};
	//double (*p)(double);
	
	puts("源数组:");
	show(source, LEN);
	puts("目标数组:");
	show(target, LEN); 
	
	puts("执行add函数后目标数组:");
	transform(source, target, LEN, add);
	show(target, LEN);
	
	puts("执行subtract函数后目标数组:");
	transform(source, target, LEN, subtract);
	show(target, LEN);
	
	puts("执行sqrt函数后目标数组:");
	transform(source, target, LEN, sqrt);
	show(target, LEN);
	
	puts("执行sin函数后目标数组:");
	transform(source, target, LEN, sin);
	show(target, LEN);
	
	
	puts("程序结束。");	
	return 0;
} 
double add(double x)
{
	return x + 1;
}
double subtract(double x)
{
	return x - 1;
}
void show(const double arr[], int n)
{
	int i;
	
	for (i = 0; i < n; i++)
	{
		printf("%g ", arr[i]);
	}
	printf("\n");
}
void transform(double *source, double *target, int n, double (*p)(double))
{
	int i;
	
	for (i = 0; i < n; i++)
	{
		target[i] = p(source[i]);//使用函数指针调用函数 
	}
}

输出示例:

源数组:
1 2 3 4 5 6 7 8 9 10
目标数组:
0 0 0 0 0 0 0 0 0 0
执行add函数后目标数组:
2 3 4 5 6 7 8 9 10 11
执行subtract函数后目标数组:
0 1 2 3 4 5 6 7 8 9
执行sqrt函数后目标数组:
1 1.41421 1.73205 2 2.23607 2.44949 2.64575 2.82843 3 3.16228
执行sin函数后目标数组:
0.841471 0.909297 0.14112 -0.756802 -0.958924 -0.279415 0.656987 0.989358 0.412118 -0.544021
程序结束。

12.问题总结

1.输入验证问题

一般是菜单选择,输入相应的字母或数字,响应相应的功能,在这里,可以把输入当成是字符串,存在一个临时数组中,必须满足输入条件;比如只想输入a才是正确的,a前面有空格或者有其他字符都不行,那么这个字符串数组第一个元素必须是a且第二个元素必须是空格;选项的获取可以使用switch,相应的输入响应相应的功能。

2.文件问题

如果一个文件要读取和写,那么要是有r+模式,空文件的判定可以用fgetc()函数,如果为空文件,就在第一次运行时把相应数据写入文件;从文件获取数据后,可以把文件先关闭,如果是要把更改后的数据再写会文件,可以用r模式打开文件,这样会清空文件数据再把想要的数据重新写入该文件。

3.指针问题

限定符const很关键,以后不需要更改数据内容的数组或结构等,作为函数参数时,数组可以用数组表示法,这当然不会改变数组本身内容,不需要加const,但是用指针表示法或者结构作为函数参数时,要加上const,以免更改了内容编译器也不提醒。

你可能感兴趣的:(《C,Primer,Plus》,c语言,算法,学习,开发语言,程序人生)