第十三章编程练习
对于文件的操作是程序开发过程中必不可少的。首先,来看一下第一题,对13.1程序进行修改,输入文件名,而不是命令行参数。完整程序代码以及运行结果如下:
#include<stdio.h>
#include<stdlib.h>
int main(void){int ch;FILE * fp;unsigned long count = 0;char name[44];printf("请输入要打开的文件名:\n");scanf("%s",name);if ((fp = fopen(name,"r")) == NULL){printf("Can't open %s\n",name);exit(EXIT_FAILURE);}while ((ch = getc(fp)) != EOF){putc(ch,stdout);count++;}fclose(fp);printf("File %s has %lu characters\n",name,count);return 0;
}
下面,来看一下第二题,编写一个文件拷贝程序,通过命令行获取两个文件名,完整程序代码以及运行结果如下
#include<stdio.h>
#include<stdlib.h>
#define SIZE 44
int main(int argc, char * argv[]){int ch;FILE * in, * out;if(argc < 2){fprintf(stderr,"Usage: %s filename\n",argv[0]);exit(EXIT_FAILURE);}if ((in = fopen(argv[1], "r")) == NULL){fprintf(stderr,"I couldn't open the file \"%s\"\n",argv[1]);}if((out = fopen(argv[2],"w")) == NULL){fprintf(stderr,"Can't open or create the file \"%s\"\n",argv[2]);exit(EXIT_FAILURE);}while ((ch = getc(in)) != EOF){putc(ch,out);}if (fclose(in) != 0 || fclose(out) != 0){fprintf(stderr,"Error in close files\n");}return 0;
}
下面来看一下第三题,还是写一个文件拷贝程序,但是是让用户输入文件名,然后以该文件名作为原始文件名和输出文件名,然后将文本全部转换成大写。完整程序代码以及运行结果如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define SIZE 44
#define PF "D:\\C\\"
#define Y "D:\\C\\a.txt"
int main(){int ch;FILE * in, * out;char name[SIZE];char pf[SIZE] = PF;printf("请输入文件名:\n");scanf("%s",name);strcat(pf,name);if ((in = fopen(Y, "r")) == NULL){fprintf(stderr,"I couldn't open the file \"%s\"\n",Y);}if((out = fopen(pf,"w+")) == NULL){fprintf(stderr,"Can't open or create the file \"%s\"\n",pf);exit(EXIT_FAILURE);}while ((ch = getc(in)) != EOF){putc(toupper(ch),out);}if (fclose(in) != 0 || fclose(out) != 0){fprintf(stderr,"Error in close files\n");}return 0;
}
接下来,来看一下第四题,要求用命令行参数去展示所有的文件,完整程序代码以及运行结果如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define SIZE 44
int main(int argc,char * argv[]){int ch;FILE * in, * out;for (int i = 1; i < argc; i++){if ((in = fopen(argv[i], "r")) == NULL){fprintf(stderr,"I couldn't open the file \"%s\"\n",argv[i]);}while ((ch = getc(in)) != EOF){putc(ch,stdout);}printf("\n");if (fclose(in) != 0){fprintf(stderr,"Error in close files\n");}}return 0;
}
下面,我们来看一下第五题的要求,让我们将清单13.5的程序修改为命令行界面。将需要获取的文件名通过命令去赋值即可,完整程序代码以及运行结果如下:
/* append.c -- 把文件附加到另一个文件末尾 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 4096
#define SLEN 81
void append(FILE *source, FILE *dest);
char * s_gets(char * st, int n);int main(int argc, char * argv[]){FILE *fa, *fs; // fa 指向目标文件,fs 指向源文件int files = 0; // 附加的文件数量//char file_app[SLEN]; // 目标文件名//char file_src[SLEN]; // 源文件名int ch;if (argc < 2){fprintf(stderr,"Usage: %s filename\n",argv[0]);exit(EXIT_FAILURE);}//puts("Enter name of destination file:");//s_gets(file_app, SLEN);if ((fa = fopen(argv[1], "a+")) == NULL){fprintf(stderr, "Can't open %s\n", argv[1]);exit(EXIT_FAILURE);}if (setvbuf(fa, NULL, _IOFBF, BUFSIZE) != 0){fputs("Can't create output buffer\n", stderr);exit(EXIT_FAILURE);}puts("Enter name of first source file (empty line to quit):");int i = 2;while (i < argc){if (strcmp(argv[2], argv[1]) == 0)fputs("Can't append file to itself\n", stderr);else if ((fs = fopen(argv[2], "r")) == NULL)fprintf(stderr, "Can't open %s\n", argv[2]);else{if (setvbuf(fs, NULL, _IOFBF, BUFSIZE) != 0){fputs("Can't create input buffer\n", stderr);continue;}append(fs, fa);if (ferror(fs) != 0)fprintf(stderr, "Error in reading file %s.\n",argv[2]);if (ferror(fa) != 0)fprintf(stderr, "Error in writing file %s.\n",argv[1]);fclose(fs);files++;printf("File %s appended.\n", argv[1]);puts("Next file (empty line to quit):");}i++;}printf("Done appending. %d files appended.\n", files);rewind(fa);printf("%s contents:\n", argv[1]);while ((ch = getc(fa)) != EOF)putchar(ch);puts("Done displaying.");fclose(fa);return 0;
}void append(FILE *source, FILE *dest)
{size_t bytes;static char temp[BUFSIZE]; // 只分配一次while ((bytes = fread(temp, sizeof(char), BUFSIZE, source)) > 0)fwrite(temp, sizeof(char), bytes, dest);
}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'; // 在此处放置一个空字符elsewhile (getchar() != '\n')continue;}return ret_val;
}
第五题我是直接用书上的程序清单改的,有些地方提示会比较粗糙,如有需要,请自行修改文本文字。下面看一下第六题的题目要求。将命令行参数修改为提示用户输入所需信息。程序的完整代码以及运行结果如下;
// reducto.c –把文件压缩成原来的1/3!
#include <stdio.h>
#include <stdlib.h> // 提供 exit()的原型
#include <string.h> // 提供 strcpy()、strcat()的原型
#define LEN 40
#define PF "D:\\C\\"int main()
{FILE *in, *out; // 声明两个指向 FILE 的指针int ch;char name[LEN]; // 存储输出文件名int count = 0;char inn[LEN]; // 存储输出文件名char pfin[LEN] = PF; // 存储输出文件名printf("请输入文件名:\n");scanf("%s",inn);strcat(pfin,inn);// 设置输入if ((in = fopen(pfin, "r")) == NULL){fprintf(stderr, "I couldn't open the file \"%s\"\n",pfin);exit(EXIT_FAILURE);}// 设置输出char innn[LEN];strcpy(innn,inn);int i = 0;while (innn[i] != '\0'){if(innn[i] == '.'){innn[i] = '_';}i++;} strncpy(name, innn, LEN - 5); // 拷贝文件名name[LEN - 5] = '\0';strcat(name, ".red"); // 在文件名后添加.redchar pfout[LEN] = PF; // 存储输出文件名strcat(pfout,name);if ((out = fopen(pfout, "w")) == NULL){ // 以写模式打开文件fprintf(stderr, "Can't create output file.\n");exit(3);}// 拷贝数据while ((ch = getc(in)) != EOF)if (count++ % 3 == 0)putc(ch, out); // 打印3个字符中的第1个字符// 收尾工作if (fclose(in) != 0 || fclose(out) != 0)fprintf(stderr, "Error in closing files\n");return 0;
}
接下来,来看一下第七题,打开两个文件,文件名可以用命令行或者输入都可以,然后有两个打印顺序。完整程序代码以及运行结果如下:
样式a结果
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define SIZE 44
#define SIZE2 100
int main(int argc,char * argv[]){char ch[SIZE2];char ch2[SIZE2];FILE * in, * out;if (argc < 2){fprintf(stderr,"Usage: %s filename\n",argv[0]);exit(EXIT_FAILURE);}if ((in = fopen(argv[1], "r")) == NULL){fprintf(stderr,"I couldn't open the file \"%s\"\n",argv[1]);exit(EXIT_FAILURE);}if((out = fopen(argv[2],"r")) == NULL){fprintf(stderr,"Can't open the file \"%s\"\n",argv[2]);exit(EXIT_FAILURE);}int i = 1 ,j = 1;while (i == 1 || j == 1){if (fgets(ch,SIZE2,in) != NULL){fputs(ch,stdout);}else{i = 0;}if (fgets(ch2,SIZE2,out) != NULL){fputs(ch2,stdout);}else{j = 0;}if (i == 0|| j == 0){printf("\n");}}if (fclose(in) != 0||fclose(out) != 0){//运行的时候忘记关闭out文件流了,后来补充的fprintf(stderr,"Error in close files\n");}return 0;
}
样式b结果:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define SIZE 44
#define SIZE2 100
int main(int argc,char * argv[]){char ch[SIZE2];char ch2[SIZE2];FILE * in, * out;if (argc < 2){fprintf(stderr,"Usage: %s filename\n",argv[0]);exit(EXIT_FAILURE);}if ((in = fopen(argv[1], "r")) == NULL){fprintf(stderr,"I couldn't open the file \"%s\"\n",argv[1]);exit(EXIT_FAILURE);}if((out = fopen(argv[2],"r")) == NULL){fprintf(stderr,"Can't open the file \"%s\"\n",argv[2]);exit(EXIT_FAILURE);}int i = 1 ,j = 1;while (i == 1 || j == 1){int w = 0;while (ch[w] != '\0'){ch[w] = '\0';w++;}w = 0;while (ch2[w] != '\0'){ch2[w] = '\0';w++;}if (fgets(ch,SIZE2,in) != NULL){i = 1;if (j == 1){char * find = strchr(ch,'\n');*find = '\0';}}else{i = 0;}if (fgets(ch2,SIZE2,out) != NULL && j==1){strcat(ch,ch2);}else{j = 0;}fputs(ch,stdout);}if (fclose(in) != 0 || fclose(out) != 0){fprintf(stderr,"Error in close files\n");}return 0;
}
接下来,我们来看一下第八题的要求,编写一个可以循环依次计算命令行中文件中指定字符的个数,如果没有文件参数的话,就从标准输入中获取需要计算的内容。完整程序代码以及运行结果如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define SIZE 100
int main(int argc, char * argv[]){FILE * in;char * c = argv[1];char arr[SIZE];int count;char ch;if (argc < 2){fprintf(stderr,"Usage: %s filename\n", argv[0]);exit(EXIT_FAILURE);}if (argc == 2){printf("请输入您需要回显的内容:\n");fgets(arr,SIZE,stdin);fputs(arr,stdout);count = 0;for (int i = 0; i < strlen(arr); i++){if (arr[i] == *c){count++;}}fprintf(stdout,"字符%c在输入中共出现了%d次!\n",*c,count);}for (int i = 2; i < argc; i++){if ((in = fopen(argv[i],"r")) == NULL){fprintf(stderr,"I couldn't open the file \"%s\"\n",argv[i]);continue;}count = 0;while ((ch = getc(in)) != EOF){if (ch == *c){count++;}}fprintf(stdout,"字符%c在文件%s中共出现了%d次!\n",*c,argv[i],count); if (fclose(in) != 0){fprintf(stderr,"Error in close files\n");}}return 0;
}
接着,我们来看第九题的题目要求,对程序清单13.3进行修改,在前面加一个单词编号,并且每次都在更新编号,完整程序代码以及运行结果如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 41int main(void)
{FILE *fp;char words[MAX];int number = 0;if ((fp = fopen("wordy", "a+")) == NULL){fprintf(stdout, "Can't open \"wordy\" file.\n");exit(EXIT_FAILURE);}rewind(fp);while (fgets(words,MAX,fp) != NULL){number++;}rewind(fp);puts("Enter words to add to the file; press the #");puts("key at the beginning of a line to terminate.");while ((fscanf(stdin, "%40s", words) == 1) && (words[0] != '#'))fprintf(fp, "%d: %s\n", ++number, words);puts("File contents:");rewind(fp); /* 返回到文件开始处 */while (fgets(words,MAX,fp) != NULL){fputs(words,stdout);}puts("Done!");if (fclose(fp) != 0)fprintf(stderr, "Error closing file\n");return 0;
}
下面,我们来看一下第十题的题目要求,需要我们去展示文件位置到第一个换行符之间的内容。完整程序代码以及运行结果如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define SIZE 44
#define PF "D:\\C\\"
int main(void){FILE * in;char name[SIZE];char pfname[SIZE] = PF;int number;char ch;printf("请输入您要查询的文件名:\n");scanf("%s",name);strcat(pfname,name);if ((in = fopen(pfname,"r")) == NULL){fprintf(stderr,"I couldn't open the file \"%s\"\n",pfname);exit(EXIT_FAILURE);}printf("请输入您要展示的文件位置:\n");while (scanf("%d",&number) == 1 && number > 0){fseek(in,(long)number,SEEK_SET);while ((ch = getc(in)) != '\n'){putchar(ch);}rewind(in);printf("\n请再次输入需要展示的文件位置:\n");}printf("Done!");return 0;
}
接下来,便是第十一题的内容了,我们先看一下这道题目的具体要求是什么。接受两个命令行参数,然后一个字符串,一个文件名,打印出文件中包含字符串的所有行的内容,文件中的所有行都不会超过255个字符。完整程序代码以及运行结果如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define SIZE 255
int main(int argc, char * argv[]){FILE * in;char words[SIZE];if (argc < 2){fprintf(stderr,"Usage: %s filename\n",argv[0]);exit(EXIT_FAILURE);}if ((in = fopen(argv[2],"r")) == NULL){fprintf(stderr,"I couldn't open the file \"%s\"\n",argv[2]);exit(EXIT_FAILURE);}while (fgets(words,SIZE,in) != NULL){if (strstr(words,argv[1])){fputs(words,stdout);} }if (fclose(in) != 0){fprintf(stderr,"Error in closing files\n");}return 0;
}
下面,我们来看一下第十二题要求我们实现一些什么功能
让我们将数字转换为图像字符,完整程序代码以及运行结果如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define NAME "D:\\C\\test.txt"
void zhuan(char (* s)[31], int (* a)[30]);
int main(void){FILE * in,* out;int arr[20][30];char sc[20][31];char name[44];if ((in = fopen(NAME,"r")) == NULL){fprintf(stderr,"I could't open the file %s\n",NAME);exit(EXIT_FAILURE);}int (* a)[30] = arr;char (* s)[31] = sc;for (int i = 0; i < 20; i++){for (int j = 0; j < 30; j++){fscanf(in,"%d",(*(a+i)+j));}}zhuan(s,a);printf("请输入您要存储的文件名:\n");scanf("%s",name);while ((out = fopen(name,"w+")) == NULL){fprintf(stderr,"I could't create the file %s\n",name);printf("请再次输入您要存储的文件名:\n");scanf("%s",name);}for (int i = 0; i < 20; i++){for (int j = 0; j < 31; j++){putchar(sc[i][j]);putc(sc[i][j],out);//printf("%c",sc[i][j]);}printf("\n");putc('\n',out);}if (fclose(in) != 0 || fclose(out) != 0){fprintf(stderr,"Error in close files\n");}return 0;
}
void zhuan(char (* s)[31],int (* a)[30]){for (int i = 0; i < 20; i++){for (int j = 0; j < 31; j++){if (j == 30){*(*(s+i)+j) = '\0';continue;}switch (*(*(a+i)+j)){case 0:*(*(s+i)+j) = ' ';break;case 1:*(*(s+i)+j) = '.';break;case 2:*(*(s+i)+j) = '\'';break;case 3:*(*(s+i)+j) = ':';break;case 4:*(*(s+i)+j) = '~';break;case 5:*(*(s+i)+j) = '*';break;case 6:*(*(s+i)+j) = '=';break;case 7:*(*(s+i)+j) = '@';break;case 8:*(*(s+i)+j) = '%';break;case 9:*(*(s+i)+j) = '#';break;default:break;}}}
}
接下来,我们来看一下第十三题的要求,让我们用变长数组代替标准数组去修改上一个编程练习。将程序中的数组长度修改为变量值,如有需要,可以根据文件长度去设置变量值,这里暂时不提,完整程序代码以及运行结果如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define NAME "D:\\C\\test.txt"
void zhuan(int m, int r, int n, char (* s)[r], int (* a)[n]);
int main(void){FILE * in,* out;int m = 20;int n = 30;int r = 31;int arr[m][n];char sc[m][r];char name[44];if ((in = fopen(NAME,"r")) == NULL){fprintf(stderr,"I could't open the file %s\n",NAME);exit(EXIT_FAILURE);}int (* a)[n] = arr;char (* s)[r] = sc;for (int i = 0; i < m; i++){for (int j = 0; j < n; j++){fscanf(in,"%d",(*(a+i)+j));}}zhuan(m,r,n,s,a);printf("请输入您要存储的文件名:\n");scanf("%s",name);while ((out = fopen(name,"w+")) == NULL){fprintf(stderr,"I could't create the file %s\n",name);printf("请再次输入您要存储的文件名:\n");scanf("%s",name);}for (int i = 0; i < m; i++){for (int j = 0; j < r; j++){putchar(sc[i][j]);putc(sc[i][j],out);//printf("%c",sc[i][j]);}printf("\n");putc('\n',out);}if (fclose(in) != 0 || fclose(out) != 0){fprintf(stderr,"Error in close files\n");}return 0;
}
void zhuan(int m, int r, int n, char (* s)[r],int (* a)[n]){for (int i = 0; i < m; i++){for (int j = 0; j < r; j++){if (j == 30){*(*(s+i)+j) = '\0';continue;}switch (*(*(a+i)+j)){case 0:*(*(s+i)+j) = ' ';break;case 1:*(*(s+i)+j) = '.';break;case 2:*(*(s+i)+j) = '\'';break;case 3:*(*(s+i)+j) = ':';break;case 4:*(*(s+i)+j) = '~';break;case 5:*(*(s+i)+j) = '*';break;case 6:*(*(s+i)+j) = '=';break;case 7:*(*(s+i)+j) = '@';break;case 8:*(*(s+i)+j) = '%';break;case 9:*(*(s+i)+j) = '#';break;default:break;}}}
}
最后,我们来看一下最后一个问题,在上面的基础上写一个函数去做图像失真处理,四个边上的值需要做一下特殊处理,因为相邻的值只有三个。完整程序代码以及运行结果如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define NAME "D:\\C\\test.txt"
void zhuan(int m, int r, int n, char (* s)[r], int (* a)[n]);
int shiZhen(int x, int y, int m,int n, int (* a)[n]);
int main(void){FILE * in,* out;int m = 20;int n = 30;int r = 31;int arr[m][n];int arnew[m][n];char sc[m][r];char name[44];if ((in = fopen(NAME,"r")) == NULL){fprintf(stderr,"I could't open the file %s\n",NAME);exit(EXIT_FAILURE);}int (* a)[n] = arr;char (* s)[r] = sc;for (int i = 0; i < m; i++){for (int j = 0; j < n; j++){fscanf(in,"%d",(*(a+i)+j));}}for (int i = 0; i < m; i++){for (int j = 0; j < n; j++){arnew[i][j] = shiZhen(i,j,m,n,arr);} }int (* b)[n] = arnew;zhuan(m,r,n,s,b);printf("请输入您要存储的文件名:\n");scanf("%s",name);while ((out = fopen(name,"w+")) == NULL){fprintf(stderr,"I could't create the file %s\n",name);printf("请再次输入您要存储的文件名:\n");scanf("%s",name);}for (int i = 0; i < m; i++){for (int j = 0; j < n; j++){printf("%d",arr[i][j]);}printf("\n");}printf("\n");for (int i = 0; i < m; i++){for (int j = 0; j < n; j++){printf("%d",arnew[i][j]);}printf("\n");}for (int i = 0; i < m; i++){for (int j = 0; j < r; j++){putchar(sc[i][j]);putc(sc[i][j],out);//printf("%c",sc[i][j]);}printf("\n");putc('\n',out);}if (fclose(in) != 0 || fclose(out) != 0){fprintf(stderr,"Error in close files\n");}return 0;
}
int shiZhen(int x, int y, int m,int n, int (* a)[n]){int sum = 0,count = 0;//计算总和以及周围数据个数int number = *(*(a+x)+y);//获取当前下标下原数组的值int flagu = 1,flagd = 1,flagl = 1,flagr = 1;//是否做失真处理if (x != 0){//不是最上面一行,就会有上方的值存在int up = *(*(a+x-1)+y);//获取数据上方的值if (number-up>1||up-number>1){flagu = 1;}else{flagu = 0;}sum += up; count++;}if (x<m-1){//不是最后一行,就会有下侧数据存在int down = *(*(a+x+1)+y);//获取数据下方的值if (number-down>1||down-number>1){flagd = 1;}else{flagd = 0;}sum += down;count++;}if (y != 0){//不是最左侧一行,就会有左侧数据存在int left = *(*(a+x)+y-1);//获取数据左侧的值if ((number-left)>1||(left-number)>1){flagl = 1;}else{flagl = 0;}sum += left;count++;}if (y < n-1){//不是最右侧一行,就会有右侧数据存在int right = *(*(a+x)+y+1);//获取数据右侧的值if ((number-right)>1||(right-number)>1){flagr = 1;}else{flagr = 0;}sum += right;count++;}if (flagu && flagd && flagl && flagr){return (int)(sum/count);}else{return number;}
}
void zhuan(int m, int r, int n, char (* s)[r],int (* a)[n]){for (int i = 0; i < m; i++){for (int j = 0; j < r; j++){if (j == 30){*(*(s+i)+j) = '\0';continue;}switch (*(*(a+i)+j)){case 0:*(*(s+i)+j) = ' ';break;case 1:*(*(s+i)+j) = '.';break;case 2:*(*(s+i)+j) = '\'';break;case 3:*(*(s+i)+j) = ':';break;case 4:*(*(s+i)+j) = '~';break;case 5:*(*(s+i)+j) = '*';break;case 6:*(*(s+i)+j) = '=';break;case 7:*(*(s+i)+j) = '@';break;case 8:*(*(s+i)+j) = '%';break;case 9:*(*(s+i)+j) = '#';break;default:break;}}}
}
结果中分别是处理之前以及处理之后的数据内容,为了明显的看一下效果,所以在控制台打印了一下,其实这部分是没必要展示的,如果有需要的话,可以自行删掉。好,以上就是第十三章的编程练习的全部内容,着实废了一些时间,不过秉着磨刀不误砍柴工的想法,慢慢研究慢慢做,总会有效果的。总之,在那发生之前,要多想。