C Primer Plus第十一章第十一题详解
首先,分析一下要求以及如何对其进行实现,读入十个字符串或者读到文件结尾结束,然后提供一个含有5个选项的菜单分别以四种格式去打印字符串列表,以及退出,循环展示菜单,分别按照对应的格式去打印不同格式的字符串序列。首先我们应该创建一个展示菜单的函数:
c
void showMenu(void){
printf("*****************************************************\n");
printf("请输入您要选择的操作:\n");
printf("1:打印原字符列表 2:以ASCII中的顺序打印字符串\n");
printf("3:按长度递增顺序打印 4:按第一个单词的长度打印 \n");
printf("5:退出\n");
printf("*****************************************************\n");
}
上面的函数简单的创建了一个菜单,只要循环调用该函数,就可以多次打印出这个菜单。接下来,既然有菜单了,那么我们就要获取用户选择菜单上的值,以确定接下来要根据那个格式去打印字符串序列,所以我们再写一个获取操作参数的函数:
c
int getCz(void){
int input;
while (scanf("%d",&input)!=1||input<1||input>5)
{
printf("您输入的值不合法,请重新输入!!\n");
}
return input;
}
上面这个函数对用户选择的操作参数进行获取并简单的对其进行校验,以免混入其他数据,只要在展示菜单之后,调用这一个函数,就可以获取用户选择的操作了。接下来,我们就可以依次实现四种不同的打印格式的函数了,首先是第一种打印格式--源字符串列表,将我们获取到的字符串列表数组按照循环依次打印展示即可,完整函数代码如下 :
c
void showa(char (* a)[SIZE], int n){
printf("源字符串列表:\n");
for (int i = 0; i <= n; i++){
fputs(*(a+i), stdout);
}
}
然后是第二个打印格式--按照ASCII码顺序打印,看似很简单,实际上实现这个功能并不是很容易,经过好多次的调试之后,才得到目前这一版可用的函数。因为后续有可能还会展示原格式,所以我们排序的时候最好不要更改原格式,我们在函数里面创建一个用于存储该格式的数组。说到这呢,我们其实还可以优化一下,因为程序是循环获取操作参数,有可能反复调用同一参数,我们可以再创建几个参数去记录此种格式是否已经被排序,另外将这几种格式的记录数组放置到几个小函数的外面,比如主函数中。如果已经排序的话,直接展示排序之后的数组即可,这个我们就先不实现了。我们将排序之后的记录数组放到小函数中,无论是否排序过,都进行新一次操作。说回这个函数,我们首先将原数据数组拷贝到函数独有的数组中,再通过strcmp函数对其进行机器排序,再然后调换顺序,进行打印。该打印格式的函数如下:
c
void showb(char (* a)[SIZE], int n){
char arrb[ROWS][SIZE];
char (* acb)[SIZE] = arrb;
char ac[SIZE];
for (int i = 0; i <= n; i++){
strcpy(*(acb+i),*(a+i));
}
for (int i = 0; i < n; i++){
for (int j = i+1; j <= n; j++){
if (strcmp(*(acb+i),*(acb+j))>0){
strcpy(ac,*(acb+i));
strcpy(*(acb+i),*(acb+j));
strcpy(*(acb+j),ac);
}
}
}
printf("ASCII顺序列表:\n");
for (int i = 0; i <= n; i++){
fputs(*(acb+i), stdout);
}
}
同样,类似的,实现第三个打印格式,按照长度递增顺序打印字符串,也就是说要先根据字符串长度从小到大进行排序,大体上和上面的差不多,略有不同的一点是,我们利用strlen函数去看字符串长度,该函数的完整代码如下:
c
void showc(char (* a)[SIZE], int n){
char arrc[ROWS][SIZE];
char (* acc)[SIZE] = arrc;
char ac[SIZE];
for (int i = 0; i <= n; i++){
strcpy(*(acc+i),*(a+i));
}
for (int i = 0; i < n; i++){
for (int j = i+1; j <= n; j++){
if (strlen(*(acc+i)) > strlen(*(acc+j))){
strcpy(ac,*(acc+i));
strcpy(*(acc+i),*(acc+j));
strcpy(*(acc+j),ac);
}
}
}
printf("长度递增顺序列表:\n");
for (int i = 0; i <= n; i++){
fputs(*(acc+i), stdout);
}
}
最后是按照字符串中第一个单词的长度打印字符串,所以我们就要有一个去计算首单词长度的函数,他没说是升序还是降序,我们按照默认升序进行实现。因为没有标准函数去计算首单词的长度,所以先去搞一个计算首单词的长度的函数。搞一个参数,记录是没进入单词,还是首单词,还是已经出首单词。然后循环去看每一个进入循环的字符,然后记录首单词长度并返回。该函数如下:
c
int jiSuanWord(char * a){
int length = 0;
int flag = 0;
while (*a != '\0' && flag != 2){
if (isspace(*a) && flag == 0){
continue;
}else if (!isspace(*a) && flag == 0){
flag = 1;
length++;
}else if (!isspace(*a) && flag == 1){
length++;
}else if (isspace(*a) && flag == 1){
flag = 2;
}
a++;
}
return length;
}
最后,我们再实现第四种打印格式的函数,大体格式同上面的差不多,只是计算首字母长度使用我们自己写的函数,该函数完整代码如下:
c
void showd(char (* a)[SIZE], int n){
char arrd[ROWS][SIZE];
char (* acd)[SIZE] = arrd;
char ac[SIZE];
for (int i = 0; i <= n; i++){
strcpy(*(acd+i),*(a+i));
}
for (int i = 0; i < n; i++){
for (int j = i+1; j <= n; j++){
if (jiSuanWord(*(acd+i)) > jiSuanWord(*(acd+j))){
strcpy(ac,*(acd+i));
strcpy(*(acd+i),*(acd+j));
strcpy(*(acd+j),ac);
}
}
}
printf("首单词长度递增顺序列表:\n");
for (int i = 0; i <= n; i++){
fputs(*(acd+i), stdout);
}
}
最后,将上述的函数在主函数合适的位置进行调用,得到下面的主函数:
c
int main(){
char arr[ROWS][SIZE];
int i = 0;
int cz;
printf("请输入需要处理的字符串:\n");
while (fgets(*(arr+i),SIZE-1,stdin) != NULL && i<ROWS-1){
i++;
printf("请再次输入需要处理的字符串(输入文件结尾结束):\n");
}
showMenu();
cz = getCz();
while (cz != 5){
switch (cz)
{
case 1:
showa(arr, i);
break;
case 2:
showb(arr, i);
break;
case 3:
showc(arr, i);
break;
case 4:
showd(arr, i);
break;
default:
break;
}
showMenu();
cz = getCz();
}
printf("Done!!!\n");
getchar();
return 0;
}
最后,该题完整程序代码以及运行结果如下:
c
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#define ROWS 3
#define SIZE 100
void showMenu(void);
int getCz(void);
void showa(char (* a)[SIZE], int n);
void showb(char (* a)[SIZE], int n);
void showc(char (* a)[SIZE], int n);
int jiSuanWord(char * a);
void showd(char (* a)[SIZE], int n);
int main(){
char arr[ROWS][SIZE];
int i = 0;
int cz;
printf("请输入需要处理的字符串:\n");
while (fgets(*(arr+i),SIZE-1,stdin) != NULL && i<ROWS-1){
i++;
printf("请再次输入需要处理的字符串(输入文件结尾结束):\n");
}
showMenu();
cz = getCz();
while (cz != 5){
switch (cz)
{
case 1:
showa(arr, i);
break;
case 2:
showb(arr, i);
break;
case 3:
showc(arr, i);
break;
case 4:
showd(arr, i);
break;
default:
break;
}
showMenu();
cz = getCz();
}
printf("Done!!!\n");
getchar();
return 0;
}
void showMenu(void){
printf("*****************************************************\n");
printf("请输入您要选择的操作:\n");
printf("1:打印原字符列表 2:以ASCII中的顺序打印字符串\n");
printf("3:按长度递增顺序打印 4:按第一个单词的长度打印 \n");
printf("5:退出\n");
printf("*****************************************************\n");
}
int getCz(void){
int input;
while (scanf("%d",&input)!=1||input<1||input>5)
{
printf("您输入的值不合法,请重新输入!!\n");
}
return input;
}
void showa(char (* a)[SIZE], int n){
printf("源字符串列表:\n");
for (int i = 0; i <= n; i++){
fputs(*(a+i), stdout);
}
}
void showb(char (* a)[SIZE], int n){
char arrb[ROWS][SIZE];
char (* acb)[SIZE] = arrb;
char ac[SIZE];
for (int i = 0; i <= n; i++){
strcpy(*(acb+i),*(a+i));
}
for (int i = 0; i < n; i++){
for (int j = i+1; j <= n; j++){
if (strcmp(*(acb+i),*(acb+j))>0){
strcpy(ac,*(acb+i));
strcpy(*(acb+i),*(acb+j));
strcpy(*(acb+j),ac);
}
}
}
printf("ASCII顺序列表:\n");
for (int i = 0; i <= n; i++){
fputs(*(acb+i), stdout);
}
}
void showc(char (* a)[SIZE], int n){
char arrc[ROWS][SIZE];
char (* acc)[SIZE] = arrc;
char ac[SIZE];
for (int i = 0; i <= n; i++){
strcpy(*(acc+i),*(a+i));
}
for (int i = 0; i < n; i++){
for (int j = i+1; j <= n; j++){
if (strlen(*(acc+i)) > strlen(*(acc+j))){
strcpy(ac,*(acc+i));
strcpy(*(acc+i),*(acc+j));
strcpy(*(acc+j),ac);
}
}
}
printf("长度递增顺序列表:\n");
for (int i = 0; i <= n; i++){
fputs(*(acc+i), stdout);
}
}
int jiSuanWord(char * a){
int length = 0;
int flag = 0;
while (*a != '\0' && flag != 2){
if (isspace(*a) && flag == 0){
continue;
}else if (!isspace(*a) && flag == 0){
flag = 1;
length++;
}else if (!isspace(*a) && flag == 1){
length++;
}else if (isspace(*a) && flag == 1){
flag = 2;
}
a++;
}
return length;
}
void showd(char (* a)[SIZE], int n){
char arrd[ROWS][SIZE];
char (* acd)[SIZE] = arrd;
char ac[SIZE];
for (int i = 0; i <= n; i++){
strcpy(*(acd+i),*(a+i));
}
for (int i = 0; i < n; i++){
for (int j = i+1; j <= n; j++){
if (jiSuanWord(*(acd+i)) > jiSuanWord(*(acd+j))){
strcpy(ac,*(acd+i));
strcpy(*(acd+i),*(acd+j));
strcpy(*(acd+j),ac);
}
}
}
printf("首单词长度递增顺序列表:\n");
for (int i = 0; i <= n; i++){
fputs(*(acd+i), stdout);
}
}
需要对本题说明的是,题目要求是十个字符串组成的字符串列表,但是我在测试程序的时候一直用的是3个,因为好输入,也好看要求的格式实现了吗,屏幕也放得下。所以如果你想看一下十个字符串的效果,或者想试一下EOF的话,就将宏定义SIZE改为10,就可以对十个字符串的序列进行上述的排序了。感兴趣的可以自行去试验一下。因为这一个问题相较于其他的,比较综合,用到了不少前面所学的内容,所以详细分析一下。