目录
《C Primer Plus》编程练习
第13章
1.exercise1.c
修改程序count.c中的程序,要求提示用户输入文件名,并读取用户输入的信息,不使用命令行参数。
//exercise13.1
#include <stdio.h>
#include <stdlib.h>
#define LEN 50
int main(int argc, char *argv[])
{
int ch; // 读取文件时,存储每个字符的地方
FILE *fp; // 文件指针
unsigned long count = 0;
char filename[LEN];
printf("请输入要打开的文件名(带上后缀):\n");
scanf("%s", filename);
if ((fp = fopen(filename, "r")) == NULL)//打开文件,r表示读模式
{
printf("打不开%s文件。\n", filename);
exit(EXIT_FAILURE);
}
while ((ch = getc(fp)) != EOF)
{
putc(ch,stdout); //与putchar(ch)相同
count++;
}
fclose(fp);//关闭文件
printf("%s文件有%lu个字符。\n", filename, count);
return 0;
}
1.txt文件是count.c程序创建的,输出示例:
请输入要打开的文件名(带上后缀):
1.txt
hello
open
1.txt文件有11个字符。
2.exercise2.c
编写一个文件拷贝程序,该程序通过命令行获取原始文件名和拷贝文件名。尽量使用标准I/O和二进制模式。
//exercise13.2
#include <stdio.h>
#include <stdlib.h>
#define BUFSIZE 4096//一次读入缓冲区的大小
int main(int argc, char *argv[])
{
FILE * source;//原始文件的文件指针
FILE * target;//目标文件的文件指针
size_t bytes;
static char temp[BUFSIZE]; //只分配一次,存放目标文件内容的临时数组
if (argc != 3)
{
printf("请输入两个命令行参数。\n");
exit(EXIT_FAILURE);
}
else
{
if ((source = fopen(argv[1], "rb")) == NULL)
{
printf("打开原始文件%s失败。\n", argv[1]);
exit(EXIT_FAILURE);
}
else
{
if ((target = fopen(argv[2], "wb")) == NULL)
{
printf("打开目标文件%s失败。\n", argv[2]);
exit(EXIT_FAILURE);
}
else
{
while ((bytes = fread(temp,sizeof(char),BUFSIZE,source)) > 0)
//temp是待读取数据块的地址;sizeof(char)表示待写入数据块的大小
//BUFSIZE表示待写入数据块的数量;source表示待读取的文件
//即把source指向文件的内容写入temp中,返回值应该是BUFSIZE
{
fwrite(temp, sizeof(char), bytes, target);
//temp是待写入数据块的地址;sizeof(char)表示待写入数据块的大小
//BUFSIZE表示待写入数据块的数量;target表示待写入的文件
//即把temp中的数据写入target指向的文件
}
if (ferror(source) != 0)//返回非零值则读写错误,返回0则读写正确
{
fprintf(stderr,"读原始文件%s错误。\n",argv[1]);
}
if (ferror(target) != 0)
{
fprintf(stderr,"写目标文件%s错误。\n",argv[2]);
}
if (fclose(source) != 0)//返回非0值则关闭文件错误
{
printf("原始文件关闭错误。\n", argv[1]);
}
if (fclose(target) != 0)
{
printf("目标文件关闭错误。\n", argv[2]);
}
}
}
}
return 0;
}
先创建 exercise2source.txt文件,在命令行下运行:
D:\桌面\C\第十三章>exercise2 exercise2source.txt exercise2target.txt
拷贝成功,打开exercise2source.txt查看是否与exercise2target.txt一样。
3.exercise3.c
编写一个文件拷贝程序,提示用户输入文本文件名,并以该文件名作为原始文件名和输出文件名。该程序要使用ctype.h中的toupper()函数,在写入到输出文件时把所有文本转换成大写。使用标准I/O和文本模式。
//exercise13.3
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
FILE * source;//原始文件的文件指针
FILE * target;//目标文件的文件指针
int ch;
if (argc != 3)
{
printf("请输入两个命令行参数。\n");
exit(EXIT_FAILURE);
}
else
{
if ((source = fopen(argv[1], "rb")) == NULL)
{
printf("打开原始文件%s失败。\n", argv[1]);
exit(EXIT_FAILURE);
}
else
{
if ((target = fopen(argv[2], "wb")) == NULL)
{
printf("打开目标文件%s失败。\n", argv[2]);
exit(EXIT_FAILURE);
}
else
{
while ((ch = getc(source)) != EOF)//把字符一个一个拿出来,直到文件末尾
{
putc(toupper(ch), target);//把字母都变成大写,放入目标文件中
}
if (ferror(source) != 0)//返回非零值则读写错误,返回0则读写正确
{
fprintf(stderr,"读原始文件%s错误。\n",argv[1]);
}
if (ferror(target) != 0)
{
fprintf(stderr,"写目标文件%s错误。\n",argv[2]);
}
if (fclose(source) != 0)//返回非0值则关闭文件错误
{
printf("原始文件关闭错误。\n", argv[1]);
}
if (fclose(target) != 0)
{
printf("目标文件关闭错误。\n", argv[2]);
}
printf("转换成功,打开%s查看是否把%s的字母全部变成大写。\n", argv[2], argv[1]);
}
}
}
return 0;
}
先创建 exercise3source.txt文件,在命令行下运行:
D:\桌面\C\第十三章>exercise3 exercise3source.txt exercise3target.txt
转换成功,打开exercise3target.txt查看是否把exercise3source.txt的字母全部变成大写。
4.exercise4.c
编写一个程序,按顺序在屏幕上显示命令行中列出的所有文件。使用argc控制循环。
//exercise13.4
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char * argv[])
{
int i;
FILE *fp;
int ch;
if (argc == 1)
{
printf("请输入命令行参数。\n");
}
else
{
for (i = 1; i < argc; i++)//循环打开每个文件
{
if ((fp = fopen(argv[i], "r")) == NULL)
{
fprintf(stderr, "%s打开错误!\n", argv[i]);
exit(EXIT_FAILURE);
}
printf("这是第%d个文件的内容:\n", i);
while ((ch = getc(fp)) != EOF)//没到文件结尾
{
putchar(ch);//打印文件的每个字符
//putc(ch, stdout);也是一样的效果
}
printf("\n");
if (fclose(fp) != 0)
{
fprintf(stderr, "%s关闭错误!\n", argv[i]);
exit(EXIT_FAILURE);
}
}
}
return 0;
}
假设创建好了exercise4_1.txt,exercise4_2.txt文件,分别存放的是英文和中文,输出示例:
D:\桌面\C\第十三章>exercise4 exercise4_1.txt exercise4_2.txt
这是第1个文件的内容:
It is the first file of exercise4.
这是第2个文件的内容:
杩欐槸exercise4鐨勭浜屼釜鏂囦欢銆
可以看出,中文的是乱码,在C语言中,一个汉字占两个字节,而char占1个字节,所以是乱码。
5.exercise5.c
修改程序append.c中的程序,用命令行界面代替交互式界面。
//exercise13.5
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 4096
void append(FILE *source, FILE *target);
int main(int argc, char *argv[])
{
FILE *source,*target; //target指向目标文件,source指向源文件
int files = 0; // 附加的文件数量
int ch;
int i;
if (argc < 3)
{
printf("请输入两个及以上命令行参数。\n");
exit(EXIT_FAILURE);
}
if ((target = fopen(argv[1], "a+")) == NULL)
{
fprintf(stderr, "目标文件%s打开错误!\n", argv[1]);
exit(EXIT_FAILURE);
}
if (setvbuf(target, NULL, _IOFBF, BUFSIZE) != 0)
{//setvbuf()创建了一个缓冲区,target是待处理的流;NULL是分配的缓冲区;
//_IOFBF是指完全缓冲;BUFSIZE是缓冲区的大小
fputs("没有成功创建缓冲区。\n", stderr);
exit(EXIT_FAILURE);
}
for (i = 2; i < argc; i++)//循环打开每个文件
{
if (strcmp(argv[1], argv[2]) == 0)
{
fputs("不能追加文件本身。\n",stderr);
}
if ((source = fopen(argv[i], "r")) == NULL)
{
fprintf(stderr, "源文件%s打开错误!\n", argv[i]);
exit(EXIT_FAILURE);
}
if (setvbuf(source, NULL, _IOFBF, BUFSIZE) != 0)
{//setvbuf()创建了一个缓冲区,source是待处理的流;NULL是分配的缓冲区;
//_IOFBF是指完全缓冲;BUFSIZE是缓冲区的大小
fputs("没有成功创建缓冲区。\n", stderr);
exit(EXIT_FAILURE);
}
append(source, target);
if (ferror(source) != 0)//返回非零值则读写错误,返回0则读写正确
{
fprintf(stderr,"读文件%s错误。\n",argv[i]);
}
if (ferror(target) != 0)
{
fprintf(stderr,"写入文件%s错误。\n",argv[1]);
}
if (fclose(source) != 0)//返回非0值则关闭文件错误
{
printf("源文件关闭错误。\n", argv[i]);
}
files++;
printf("文件%s成功追加。\n", argv[i]);
}
printf("完成%d个文件的追加。\n", files);
rewind(target);//回到目标文件开始处
printf("文件%s的内容:\n", argv[1]);
while ((ch = getc(target)) != EOF)
{
putchar(ch);
}
puts("\n程序结束。");
if (fclose(target) != 0)
{
printf("目标文件关闭错误。\n", argv[1]);
}
return 0;
}
void append(FILE *source, FILE *target)//源文件内容追加到目标文件中
{
size_t bytes;
static char temp[BUFSIZE]; //只分配一次
while ((bytes = fread(temp,sizeof(char),BUFSIZE,source)) > 0)
//temp是待读取数据块的地址;sizeof(char)表示待写入数据块的大小
//BUFSIZE表示待写入数据块的数量;source表示待读取的文件
//即把source指向文件的内容写入temp中,返回值应该是BUFSIZE
{
fwrite(temp, sizeof(char), bytes, target);
//temp是待写入数据块的地址;sizeof(char)表示待写入数据块的大小
//BUFSIZE表示待写入数据块的数量;target表示待写入的文件
//即把temp中的数据写入target指向的文件
}
}
创建两个文件exercise5source1.txt,exercise5source3.txt,输出示例:
D:\桌面\C\第十三章>exercise5 exercise5target.txt exercise5source1.txt exercise5source2.txt
文件exercise5source1.txt成功追加。
文件exercise5source2.txt成功追加。
完成2个文件的追加。
文件exercise5target.txt的内容:
It is the first file of exercise5.
It is the second file of exercise5.
程序结束。
6.exercise6.c
使用命令行参数的程序依赖于用户的内存如何正确地使用它们。重写程序reducto.c中的程序,不使用命令行参数,而是提示用户输入所需信息。
//exercise13.6
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define LEN 40
char * s_gets(char *arr, int n);
int main(void)
{
char source[LEN];
char target[LEN];
FILE *in;
FILE *out;
char ch;
int count = 0;
printf("请输入要压缩1/3的文件的文件名:\n");
s_gets(source, LEN);
if ((in = fopen(source, "r")) == NULL)
{
fprintf(stderr, "不能打开文件\"%s\"\n",source);
exit(EXIT_FAILURE);
}
strncpy(target,source, LEN - 5); // 拷贝文件名
target[LEN - 5] = '\0';
strcat(target,".red"); // 在文件名后面添加.add
if ((out = fopen(target, "w")) == NULL)
{
fprintf(stderr,"创建输出文件失败。\n");
exit(EXIT_FAILURE);
}
// 拷贝数据
while ((ch = getc(in)) != EOF)
{
if (count++ % 3 == 0)
{
putc(ch, out); // 打印3个字符中的第1个字符
}
}
printf("压缩成功,请打开%s与%s进行比较。\n", target, source);
// 收尾工作,关闭文件
if (fclose(in) != 0 || fclose(out) != 0)
{
fprintf(stderr,"关闭文件失败。\n");
}
return 0;
}
char * s_gets(char *arr, int n)
{
char * ret_val;
int i = 0;
ret_val = fgets(arr, n ,stdin);
if (ret_val != NULL)
{
while (arr[i] != '\n' && arr[i] != '\0')
{
i++;
}
if (arr[i] == '\n')
{
arr[i] = '\0';
}
else
{
while (getchar() != '\n')
{
continue;
}
}
}
}
先创建一个exercise6source文件,输出示例:
请输入要压缩1/3的文件的文件名:
exercise6source
压缩成功,请打开exercise6source.red与exercise6source进行比较。
7.exercise7.c
编写一个程序打开两个文件。可以使用命令行参数或提示用户输入文件名。
a.该程序以这样的顺序打印:打印第1个文件的第1行,第2个文件的第1行,第1个文件的第2行,第2个文件的第2行,以此类推,打印到行数较多文件的最后一行。
b.修改该程序,把行号相同的行打印成一行。
//exercise13.7a
#include <stdio.h>
#include <stdlib.h>
#define LEN 40
#define BUF 256//一行字符256个
int main(int argc,char *argv[])
{
FILE *fp1;//文件指针
FILE *fp2;
static char temp1[BUF];//暂时存放文件1的一行的数组
static char temp2[BUF];
char *st1;
char *st2;
int length1;
int length2;
if (argc == 3)
{
if ((fp1 = fopen(argv[1], "r")) == NULL)//打开文件
{
fprintf(stderr, "打开文件%s错误。\n", argv[1]);
exit(EXIT_FAILURE);
}
if ((fp2 = fopen(argv[2], "r")) == NULL)
{
fprintf(stderr, "打开文件%s错误。\n", argv[2]);
exit(EXIT_FAILURE);
}
st1 = fgets(temp1, BUF, fp1);//获取文件的一行
st2 = fgets(temp2, BUF, fp2);
while (st1 != NULL || st2 != NULL)//除非两个文件都到结尾
{
length1 = strlen(temp1);//文件某一行的长度
length2 = strlen(temp2);
if (temp1[length1 - 1] == '\n')//文件这一行最后是换行符
{
temp1[length1 - 1] = '\0';//把这一行变成数组存起来
}
if (temp2[length2 - 1] == '\n')
{
temp2[length2 - 1] = '\0';
}
if (st1 != NULL)//文件1还没读完
{
puts(temp1);//打印一行数据
}
if (st2 != NULL)
{
puts(temp2);
}
st1 = fgets(temp1, BUF, fp1);
st2 = fgets(temp2, BUF, fp2);
}
if (ferror(fp1) != 0)//读取文件是否成功
{
fprintf(stderr, "读取文件%s失败。", argv[1]);
}
if (ferror(fp2) != 0)
{
fprintf(stderr, "读取文件%s失败。", argv[2]);
}
if (fclose(fp1) != 0)//关闭文件
{
fprintf(stderr, "关闭文件%s失败。", argv[1]);
}
if (fclose(fp2) != 0)
{
fprintf(stderr, "关闭文件%s失败。", argv[2]);
}
}
else
{
printf("请输入两个命令行参数。\n");
exit(EXIT_FAILURE);
}
return 0;
}
先要有两个文件exercise7_1.txt和exercise7_2.txt,输出示例:
D:\桌面\C\第十三章>exercise7a exercise7_1.txt exercise7_2.txt
1111
2222
333
4444
55555
6
7777
9999999
其中单数的都是exercise7_1.txt的内容。如果用编译器的话,输入文件名,但是打不开该文件,可能是检索规则的原因,留待后续解决。
//exercise13.7b
#include <stdio.h>
#include <stdlib.h>
#define LEN 40
#define BUF 256
int main(int argc,char *argv[])
{
FILE *fp1;//文件指针
FILE *fp2;
static char temp1[BUF];//暂时存放文件1的一行的数组
static char temp2[BUF];
char *st1;
char *st2;
int length1;
int length2;
if (argc == 3)
{
if ((fp1 = fopen(argv[1], "r")) == NULL)//打开文件
{
fprintf(stderr, "打开文件%s错误。\n", argv[1]);
exit(EXIT_FAILURE);
}
if ((fp2 = fopen(argv[2], "r")) == NULL)
{
fprintf(stderr, "打开文件%s错误。\n", argv[2]);
exit(EXIT_FAILURE);
}
st1 = fgets(temp1, BUF, fp1);//获取文件的一行
st2 = fgets(temp2, BUF, fp2);
while (st1 != NULL || st2 != NULL)//除非两个文件都到结尾
{
length1 = strlen(temp1);//文件某一行的长度
length2 = strlen(temp2);
if (temp1[length1 - 1] == '\n')//文件这一行最后是换行符
{
temp1[length1 - 1] = '\0';//把这一行变成数组存起来
}
if (temp2[length2 - 1] == '\n')
{
temp2[length2 - 1] = '\0';
}
if (st1 != NULL)//文件1还没读完
{
printf("%s", st1);//打印,但不换行
}
if (st2 != NULL)
{
printf("%s", st2);
}
printf("\n"); //换行
st1 = fgets(temp1, BUF, fp1);
st2 = fgets(temp2, BUF, fp2);
}
if (ferror(fp1) != 0)//读取文件是否成功
{
fprintf(stderr, "读取文件%s失败。", argv[1]);
}
if (ferror(fp2) != 0)
{
fprintf(stderr, "读取文件%s失败。", argv[2]);
}
if (fclose(fp1) != 0)//关闭文件
{
fprintf(stderr, "关闭文件%s失败。", argv[1]);
}
if (fclose(fp2) != 0)
{
fprintf(stderr, "关闭文件%s失败。", argv[2]);
}
}
else
{
printf("请输入两个命令行参数。\n");
exit(EXIT_FAILURE);
}
return 0;
}
输出示例:
D:\桌面\C\第十三章>exercise7b exercise7_1.txt exercise7_2.txt
11112222
3334444
555556
7777
9999999
8.exercise8.c
编写一个程序,以一个字符和任意文件名作为命令行参数。如果字符后面没有参数,该程序读取标准输入;否则,程序依次打开每个文件并报告每个文件中该字符出现的次数。文件名和字符本身也要一同报告。程序应包含错误检查,以确定参数数量是否正确和是否能打开文件。如果无法打开文件,程序应报告这一情况,然后继续处理下一个文件。
//exercise13.8
#include <stdio.h>
#include <stdlib.h>
int count(char ch, FILE *fp);
int main(int argc, char *argv[])
{
char ch;
FILE *fp;
int total;
int i;
if (argc == 1)
{
printf("请用命令行参数运行程序。\n");
}
if (strlen(argv[1]) != 1)//第一个命令行参数不止1个字符
{
printf("第一个命令行参数只允许是一个字符。\n");
}
if (argc == 2)
{
ch = argv[1][0];
fp = stdin;
printf("请输入一串字符(以Crtl+Z结尾):\n");
total = count(ch, fp);//标准输入
printf("输入的字符串中共有%d个%c。\n", total, ch);
}
if (argc > 2)
{
ch = argv[1][0];//获取想要报告的字符
for (i = 2;i < argc; i++)
{
if ((fp = fopen(argv[i], "r")) == NULL)//打开文件
{
fprintf(stderr, "打开文件%s错误。\n", argv[i]);
exit(EXIT_FAILURE);
}
total = count(ch, fp);
printf("文件%s共有%d个%c。\n", argv[i], total, ch);
if (ferror(fp) != 0)//读取文件是否成功
{
fprintf(stderr, "读取文件%s失败。", argv[i]);
}
if (fclose(fp) != 0)//关闭文件
{
fprintf(stderr, "关闭文件%s失败。", argv[i]);
}
}
}
return 0;
}
int count(char ch, FILE *fp)//读取文件中的想要找出的字符的个数
{
int total = 0;
char ch2;
while ((ch2 =getc(fp)) != EOF)
{
if (ch == ch2)
{
total++;
}
}
return total;
}
创建文件exercise8_1.txt和exercise8_2.txt,输出示例:
exercise8 a exercise8_1.txt exercise8_2.tx exercise8_2.txt
文件exercise8_1.txt共有3个a。
打开文件exercise8_2.tx失败。
文件exercise8_2.txt共有3个a。
另一个输出示例:
D:\桌面\C\第十三章>exercise8 a
请输入一串字符(以Crtl+Z结尾):
adult apple awe
amount
^Z
输入的字符串中共有4个a。
9.exercise9.c
修改程序addaword.c中的程序,从1开始,根据加入列表的顺序为每个单词编号。当程序下次运行时,确保新的单词编号接着上次的编号开始。
//exercise13.9
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 41
int main(void)
{
FILE *fp;
char words[MAX];
int count = 0;
if ((fp = fopen("exercise9.txt", "a+")) == NULL)
{
fprintf(stdout,"Can't open \"exercise9.txt\" file.\n");
exit(EXIT_FAILURE);
}
while (fgets(words, MAX, fp) != NULL)
{
count++;//每读一个单词就计数加1
}
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", count++, words);
//单词和序号读入文件
}
puts("File contents:");
rewind(fp); /* 返回到文件开始处 */
while (fscanf(fp,"%s",words) == 1)
puts(words);
puts("Done!");
if (fclose(fp) != 0)
fprintf(stderr,"Error closing file\n");
return 0;
}
第一次运行:
Enter words to add to the file; press the #
key at the beginning of a line to terminate.
hello can you
#
File contents:
1:hello
2:can
3:you
Done!
第二次运行:
Enter words to add to the file; press the #
key at the beginning of a line to terminate.
read
it
#
File contents:
1:hello
2:can
3:you
4:read
5:it
Done!
10.exercise10.c
编写一个程序打开一个文本文件,通过交互方式获得文件名。通过一个循环,提示用户输入一个文件位置。然后该程序打印从该位置开始到下一个换行符之前的内容。用户输入负数或非数值字符可以结束输入循环。
//exercise13.10
#include <stdio.h>
#include <stdlib.h>
#define LEN 81
int main(void)
{
char filename[LEN];
FILE *fp;
long count;
char ch;
printf("请输入要打开的文本文件名(包含后缀):\n");
scanf("%s", filename);
if ((fp = fopen(filename, "rb")) == NULL)
{
fprintf(stdout, "文件%s打开失败。\n", filename);
exit(EXIT_FAILURE);
}
printf("请输入文件位置,程序打印该位置开始到下一个换行符的内容");
printf("(输入负数或非数值字符退出程序):\n");
while (scanf("%ld", &count) == 1 && count >= 0)
{
fseek(fp, count, SEEK_SET);//指定位置
ch = getc(fp);
if (ch == EOF)//先判断该位置是否超过文件结尾了
{
printf("该位置超过文件范围,请重新输入一个位置:\n");
continue;
}
while (ch != '\n' && ch != EOF)//没到换行符或者文件结尾时
{
putchar(ch);//打印
ch = getc(fp);
}
printf("\n请输入文件位置,程序打印该位置开始到下一个换行符的内容");
printf("(输入负数或非数值字符退出程序):\n");
}
if (fclose(fp) != 0)
{
fprintf(stderr,"关闭文件%s失败。\n", filename);
}
printf("程序结束。\n");
return 0;
}
输出示例:
请输入要打开的文本文件名(包含后缀):
exercise10.txt
请输入文件位置,程序打印该位置开始到下一个换行符的内容(输入负数或非数值字符退出程序):
0
this is exercise10.txt
请输入文件位置,程序打印该位置开始到下一个换行符的内容(输入负数或非数值字符退出程序):
99
该位置超过文件范围,请重新输入一个位置:
12
cise10.txt
请输入文件位置,程序打印该位置开始到下一个换行符的内容(输入负数或非数值字符退出程序):
-1
程序结束。
11.exercise11.c
编写一个程序,接受两个命令行参数。第1个参数是一个字符串,第2个参数是一个文件名。然后该程序查找该文件,打印文件中包含该字符串的所有行。因为该任务是面向行而不是面向字符的,所以要使用fgets()而不是getc()。使用标准C库函数strstr()(11.5.7节简要介绍过)在每一行中查找指定字符串。假设文件中的所有行都不超过255个字符。
//exercise13.11
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN 256
int main(int argc, char *argv[])
{
FILE *fp;
char temp[LEN];
if (argc != 3)
{
fprintf(stderr, "请用命令行输入两个正确的参数。\n", argv[0]);
exit(EXIT_FAILURE);
}
else
{
if ((fp = fopen(argv[2], "r")) == NULL)
{
fprintf(stderr, "打不开文件%s。", argv[2]);
exit(EXIT_FAILURE);
}
while (fgets(temp, LEN, fp) != NULL)//一行一行读入数组
{
if (strstr(temp, argv[1]) != NULL)//如果这一行中包含字符串
{
fputs(temp, stdout);//打印这一行
}
}
if (fclose(fp) != 0)
{
fprintf(stderr, "无法关闭文件%s。", argv[2]);
}
}
return 0;
}
exercise11.txt:
this is exercise11.txt
if
no error
close
输出示例:
D:\桌面\C\第十三章>exercise11 er exercise11.txt
this is exercise11.txt
no error
12.exercise12.c
创建一个文本文件,内含20行,每行30个整数。这些整数都在0-9之间,用空格分开。该文件是用数字表示一张图片,0-9表示逐渐增加的灰度。编写一个程序,把文件中的内容读入一个20X30的int数组中。一种把这些数字转换为图片的粗略方法是:该程序使用数组中的值初始化一个20X31的字符数组,用值0对应空格字符,1对应点字符,以此类推。数字越大表示字符所占的空间越大。例如,用#表示9。每行的最后一个字符(第31个)是空字符,这样该数组包含了20个字符串。最后,程序显示最终的图片(即,打印所有的字符串),并将结果储存在文本文件中。例如,下面是开始的数据:
0 0 9 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 2 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 9 0 0 0 0 0 0 0 5 8 9 9 8 5 5 2 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 5 8 1 9 8 5 4 5 2 0 0 0 0 0 0 0 0 0
0 0 0 0 9 0 0 0 0 0 0 0 5 8 9 9 8 5 0 4 5 2 0 0 0 0 0 0 0 0
0 0 9 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 4 5 2 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 1 8 5 0 0 0 4 5 2 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 4 5 2 0 0 0 0 0
5 5 5 5 5 5 5 5 5 5 5 5 5 8 9 9 8 5 5 5 5 5 5 5 5 5 5 5 5 5
8 8 8 8 8 8 8 8 8 8 8 8 5 8 9 9 8 5 8 8 8 8 8 8 8 8 8 8 8 8
9 9 9 9 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 3 9 9 9 9 9 9 9
8 8 8 8 8 8 8 8 8 8 8 8 5 8 9 9 8 5 8 8 8 8 8 8 8 8 8 8 8 8
5 5 5 5 5 5 5 5 5 5 5 5 5 8 9 9 8 5 5 5 5 5 5 5 5 5 5 5 5 5
0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 6 6 0 0 0 0 0 0
0 0 0 0 2 2 0 0 0 0 0 0 5 8 9 9 8 5 0 0 5 6 0 0 6 5 0 0 0 0
0 0 0 0 3 3 0 0 0 0 0 0 5 8 9 9 8 5 0 5 6 1 1 1 1 6 5 0 0 0
0 0 0 0 4 4 0 0 0 0 0 0 5 8 9 9 8 5 0 0 5 6 0 0 6 5 0 0 0 0
0 0 0 0 5 5 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 6 6 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 5 8 9 9 8 5 0 0 0 0 0 0 0 0 0 0 0 0
根据以上描述选择特定的输出字符,最终输出如下:
# *%##%*'
# *%##%**'
*%##%*~*'
# *%##%* ~*'
# *%##%* ~*'
*%##%* ~*'
*%##%* ~*'
*************%##%*************
%%%%%%%%%%%%*%##%*%%%%%%%%%%%%
#### #################:#######
%%%%%%%%%%%%*%##%*%%%%%%%%%%%%
*************%##%*************
*%##%*
*%##%* ==
'' *%##%* *= =*
:: *%##%* *=....=*
~~ *%##%* *= =*
** *%##%* ==
*%##%*
*%##%*
//exercise13.12
#include <stdio.h>
#include <stdlib.h>
#define LEN 81
#define ROWS 20
#define COLS 30
void change(int start[][COLS], char finish[][COLS], int rows);//整形数组转成字符数组
int main(void)
{
int start[ROWS][COLS];//数字读入整型数组
char finish[ROWS][COLS];//转换成字符数组
char filenamestart[LEN],filenamefinish[LEN];//转换前、后的文件名
FILE *fpstart,*fpfinish;//转换前后的文件指针
int row,col;//行、列
printf("请输入要转换的文件名:\n");
scanf("%s", filenamestart);
if ((fpstart = fopen(filenamestart, "r+")) == NULL)
{
fprintf(stderr, "打不开文件%s。\n", filenamestart);
exit(EXIT_FAILURE);
}
for (row = 0; row < ROWS; row++)//循环行
{
for (col = 0; col < COLS; col++)//循环列
{
fscanf(fpstart, "%d", &start[row][col]);//文件的数字读入整型数组
}
}
if (ferror(fpstart) != 0)
{
fprintf(stderr,"读取文件%s失败。\n",filenamestart);
}
change(start, finish, ROWS);//调用转换函数
printf("请输入转换后的文件名:\n");
scanf("%s", filenamefinish);
if ((fpfinish = fopen(filenamefinish, "w")) == NULL)
{
fprintf(stderr, "打不开文件%s。\n", filenamefinish);
exit(EXIT_FAILURE);
}
printf("转换后的图片:\n");
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
fprintf(fpfinish, "%c", finish[row][col]);//存在转换后的文件
fprintf(stdout, "%c", finish[row][col]);//在控制台输出
}
fprintf(fpfinish, "\n");//打印完一个数组就换行
fprintf(stdout, "\n");
}
if (ferror(fpfinish) != 0)
{
fprintf(stderr,"写入文件%s失败。\n",filenamefinish);
}
if (fclose(fpstart) != 0)
{
fprintf(stderr, "无法关闭文件%s。", filenamestart);
}
if (fclose(fpfinish) != 0)
{
fprintf(stderr, "无法关闭文件%s。", filenamefinish);
}
return 0;
}
void change(int start[][COLS], char finish[][COLS], int rows)
{
int row;
int col;
char change[10]= " .':~*=&%#";//数字对应的字符
for (row = 0; row < rows; row++)
{
for (col = 0; col < COLS; col++)
{
finish[row][col] = change[start[row][col]];
}
}
}
输出示例:
请输入要转换的文件名:
exercise12start.txt
请输入转换后的文件名:
exercise12finish.txt
转换后的图片:
# *%##%*'
# *%##%**'
*%.#%*~*'
# *%##%* ~*'
# *%##%* ~*'
*%#.%* ~*'
*%##%* ~*'
*************%##%*************
%%%%%%%%%%%%*%##%*%%%%%%%%%%%%
#### #################:#######
%%%%%%%%%%%%*%##%*%%%%%%%%%%%%
*************%##%*************
*%##%*
*%##%* ==
'' *%##%* *= =*
:: *%##%* *=....=*
~~ *%##%* *= =*
** *%##%* ==
*%##%*
*%##%*
用文本文档打开exercise12finish.txt文件,看上去对应不起来,但是用Notepad++打开是正确结果。可能是在文本文件中展示的效果和在控制台输出的效果不一样。
13.exercise13.c
用变长数组(VLA)代替标准数组,完成编程练习12。
//exercise13.13
#include <stdio.h>
#include <stdlib.h>
#define LEN 81
#define ROWS 20
#define COLS 30
void change(int rows, int cols, int start[rows][cols], char finish[rows][cols]);
int main(void)
{
int start[ROWS][COLS];//数字读入整型数组
char finish[ROWS][COLS];//转换成字符数组
char filenamestart[LEN],filenamefinish[LEN];//转换前、后的文件名
FILE *fpstart,*fpfinish;//转换前后的文件指针
int row,col;//行、列
printf("请输入要转换的文件名:\n");
scanf("%s", filenamestart);
if ((fpstart = fopen(filenamestart, "r+")) == NULL)
{
fprintf(stderr, "打不开文件%s。\n", filenamestart);
exit(EXIT_FAILURE);
}
for (row = 0; row < ROWS; row++)//循环行
{
for (col = 0; col < COLS; col++)//循环列
{
fscanf(fpstart, "%d", &start[row][col]);//文件的数字读入整型数组
}
}
if (ferror(fpstart) != 0)
{
fprintf(stderr,"读取文件%s失败。\n",filenamestart);
}
change(ROWS, COLS, start, finish);//调用转换函数
printf("请输入转换后的文件名:\n");
scanf("%s", filenamefinish);
if ((fpfinish = fopen(filenamefinish, "w")) == NULL)
{
fprintf(stderr, "打不开文件%s。\n", filenamefinish);
exit(EXIT_FAILURE);
}
printf("转换后的图片:\n");
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
fprintf(fpfinish, "%c", finish[row][col]);//存在转换后的文件
fprintf(stdout, "%c", finish[row][col]);//在控制台输出
}
fprintf(fpfinish, "\n");//打印完一个数组就换行
fprintf(stdout, "\n");
}
if (ferror(fpfinish) != 0)
{
fprintf(stderr,"写入文件%s失败。\n",filenamefinish);
}
if (fclose(fpstart) != 0)
{
fprintf(stderr, "无法关闭文件%s。", filenamestart);
}
if (fclose(fpfinish) != 0)
{
fprintf(stderr, "无法关闭文件%s。", filenamefinish);
}
return 0;
}
void change(int rows, int cols, int start[rows][cols], char finish[rows][cols])
//变长数组
{
int row;
int col;
char change[10]= " .':~*=&%#";//数字对应的字符
for (row = 0; row < rows; row++)
{
for (col = 0; col < cols; col++)
{
finish[row][col] = change[start[row][col]];
}
}
}
只需要改一下变长数组即可。输出示例:
请输入要转换的文件名:
exercise13start.txt
请输入转换后的文件名:
exercise13finish.txt
转换后的图片:
# *%##%*'
# *%##%**'
*%.#%*~*'
# *%##%* ~*'
# *%##%* ~*'
*%#.%* ~*'
*%##%* ~*'
*************%##%*************
%%%%%%%%%%%%*%##%*%%%%%%%%%%%%
#### #################:#######
%%%%%%%%%%%%*%##%*%%%%%%%%%%%%
*************%##%*************
*%##%*
*%##%* ==
'' *%##%* *= =*
:: *%##%* *=....=*
~~ *%##%* *= =*
** *%##%* ==
*%##%*
*%##%*
14.exercise14.c
数字图像,尤其是从宇宙飞船发回的数字图像,可能会包含一些失真。为编程练习12添加消除失真的函数。该函数把每个值与它上下左右相邻的值作比较,如果该值与其周围相邻值的差都大于1,则用所有相邻值的平均值(四舍五入为整数)代替该值。注意,与边界上的点相邻的点少于4个,所以做特殊处理。
//exercise13.14
#include <stdio.h>
#include <stdlib.h>
#define LEN 81
#define ROWS 20
#define COLS 30
void change(int start[][COLS], char finish[][COLS], int rows);
int update(int start[][COLS], int rows, int row, int col);
int main(void)
{
int start[ROWS][COLS];//数字读入整型数组
char finish[ROWS][COLS];//转换成字符数组
char filenamestart[LEN],filenamefinish[LEN];//转换前、后的文件名
FILE *fpstart,*fpfinish;//转换前后的文件指针
int row,col;//行、列
printf("请输入要转换的文件名:\n");
scanf("%s", filenamestart);
if ((fpstart = fopen(filenamestart, "r+")) == NULL)
{
fprintf(stderr, "打不开文件%s。\n", filenamestart);
exit(EXIT_FAILURE);
}
for (row = 0; row < ROWS; row++)//循环行
{
for (col = 0; col < COLS; col++)//循环列
{
fscanf(fpstart, "%d", &start[row][col]);//文件的数字读入整型数组
}
}
if (ferror(fpstart) != 0)
{
fprintf(stderr,"读取文件%s失败。\n",filenamestart);
}
for (row = 0; row < ROWS; row++)//循环行
{
for (col = 0; col < COLS; col++)//循环列
{
start[row][col] = update(start, ROWS, row, col);//消除失真
}
}
change(start, finish, ROWS);//调用转换函数
printf("请输入转换后的文件名:\n");
scanf("%s", filenamefinish);
if ((fpfinish = fopen(filenamefinish, "w")) == NULL)
{
fprintf(stderr, "打不开文件%s。\n", filenamefinish);
exit(EXIT_FAILURE);
}
printf("转换后的图片:\n");
for (row = 0; row < ROWS; row++)
{
for (col = 0; col < COLS; col++)
{
fprintf(fpfinish, "%c", finish[row][col]);//存在转换后的文件
fprintf(stdout, "%c", finish[row][col]);//在控制台输出
}
fprintf(fpfinish, "\n");//打印完一个数组就换行
fprintf(stdout, "\n");
}
if (ferror(fpfinish) != 0)
{
fprintf(stderr,"写入文件%s失败。\n",filenamefinish);
}
if (fclose(fpstart) != 0)
{
fprintf(stderr, "无法关闭文件%s。", filenamestart);
}
if (fclose(fpfinish) != 0)
{
fprintf(stderr, "无法关闭文件%s。", filenamefinish);
}
return 0;
}
void change(int start[][COLS], char finish[][COLS], int rows)
{
int row;
int col;
char change[10]= " .':~*=&%#";//数字对应的字符
for (row = 0; row < rows; row++)
{
for (col = 0; col < COLS; col++)
{
finish[row][col] = change[start[row][col]];
}
}
}
int update(int start[][COLS], int rows, int row, int col)
{
int i;
int total = 0;
int count = 0;
int note = 0;
int change[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
//该数组用于表示四周的行列号
int change_x;
int change_y;
int difference;
for (i = 0; i < 4; ++i)//上下左右的值
{
change_x = row + change[i][0];//i=0时,row不变
change_y = col + change[i][1];//i=0时,col变,即右边的值
if (change_x < 0 || change_x >= ROWS || change_y < 0 || change_y >= COLS)
{//边界值
continue;//此处无点,下一轮循环
}
note++;//记录周围有几个点
difference = start[row][col] - start[change_x][change_y];//该点与周围的差值
if (difference > 1 || difference < -1)//差值超过1
{
total += start[change_x][change_y];//该点周围值的总和
count++;//记录差值大于1的点的个数
}
}
if (count != note)//周围的点与该值的差值大于1的点不一致
{
return start[row][col];//该点的值不用变,返回该点的值
}
else
{
start[row][col] = 1.0 * total / count + 0.5;//计算周围值的平均值来代替该点
return start[row][col];
}
}
输出示例:
请输入要转换的文件名:
exercise14start.txt
请输入转换后的文件名:
exercise14finish.txt
转换后的图片:
*%##%*:
*%##%**:
*%##%*~*:
*%##%* ~*:
*%##%* ~*:
*%##%* ~*:
*%##%* ~*:
*************%##%*************
%%%%%%%%%%%%*%##%*%%%%%%%%%%%%
##############################
%%%%%%%%%%%%*%##%*%%%%%%%%%%%%
*************%##%*************
*%##%*
*%##%* ==
'' *%##%* *= =*
:: *%##%* *=....=*
~~ *%##%* *= =*
** *%##%* ==
*%##%*
*%##%*
本文详细介绍了《C Primer Plus》第13章的编程练习,涵盖了从用户输入文件名到文件操作,包括文件拷贝、字符计数、行合并、错误检查等多个方面,通过实例代码展示了C语言在处理文件和输入输出时的技巧和注意事项。

1522

被折叠的 条评论
为什么被折叠?



