《C Primer Plus》第6版 编程练习 第十三章 统一发

版权声明:此文为博主原创文章,转载请注明出处 https://blog.csdn.net/Alex_mercer_boy/article/details/81978835

《C Primer Plus》第6版 编程练习 第十三章

目录 (如果该目录无法使用,右侧有个目录选项也可以使用,针对 PC端 用户

            使用教程 (戳这里)) 

先看这里(声明):

函数原型:

    fopen_()   :

    fgetc() 和 fputc():

    rewind() 函数:

源码 + 题目 + 运行效果:

Pe13-1:

Pe13-2:

Pe13-3:

Pe13-4:

Pe13-5:

Pe13-6:

Pe13-7:


先看这里(声明):

本书中所有使用的 fpoen() 函数由于 VS2017 提示不安全

所以全部使用 fopen_s() 函数代替,函数原型 下面会一一展示出来。

恳请各位大佬使用 PC 端 查看,美感,很重要的!!!

编译环境 : VS2017 Community 

代码可以直接复制编译

函数原型:

    fopen_()   :

    _ACRTIMP errno_t __cdecl fopen_s(
        _Outptr_result_maybenull_ FILE**      _Stream,
        _In_z_                    char const* _FileName,
        _In_z_                    char const* _Mode
        );

简单的说 就是:

返回值是 errno_t 类型,实际为int 类型,但我就用 errno_t 直接定义

typedef int     errno_t;

所以后面的 errno_t 直接当作 int 就行了。

fopen_s() 函数

如果成功返回0,失败则返回相应的错误代码。

开个玩笑哈!这是我测试的时候会这样(已经解决),下面是实际情况(我在 exit() 前加了 system("pause");

一般情况下是一闪而过,直接退出的

还有就是传入的第一个参数 取文件指针的地址

	FILE * fp;	        //文件指针
        errno_t err;	        //fopen_s() 返回errno_t (int)类型
	char filename[30];	//储存输入的文件名

        /*获取文件名...*/

        //第一个参数取文件指针的地址
        //第二个参数取要打开文件的名称
        //第三个参数不变,取模式
	if ((err = fopen_s(&fp, filename, "r")) != 0)
        
        /*一大堆.....结尾*/



    fgetc() 和 fputc():

        fgetc():
        int fgetc (FILE * stream );

        fgetc():
            返回指定流的内部文件位置指示器当前指向的字符。然后将内部文件位置指示器推进到下一个字符。
            如果在调用时流位于文件末尾, 则该函数返回 EOF 并设置流的文件结尾指示器 (feof).    (转至MSDN)

	fputc():
	int fputc (int character, FILE * stream );

        fputc():

            将字符写入流并前进位置指示器。
            字符被写在由流的内部位置指示器指示的位置, 然后由一个自动推进.  (转至MSDN)

rewind() 函数:

rewind() (C库函数)功能:

将文件内部的指针重新指向一个流的开头

注意:不是文件指针而是文件内部的位置指针,

随着对文件的读写文件的位置指针(指向当前读写字节)向后移动。

而文件指针是指向整个文件,如果不重新赋值文件指针不会改变。

如果需要的,可以参考下面的 Pe13-7 练习题 或自行百度

源码 + 题目 + 运行效果:

Pe13-1:

/*    

    13-1 ----修改程序清单 13.1 中的程序,
    要求提示用户输入文件名,并读取用户输入的信息,不使用命令行参数。

*/

#include <stdio.h>
#include <stdlib.h>	//提供 exit() 的原型
#define FNSIZE 30	//用户输入文件名的长度

int main(void)
{
	int ch;			        //读取文件时,储存每个字符的地方
	FILE * fp;		        //文件指针
	errno_t err;			//fopen_s() 返回errno_t (int)类型
	unsigned long count = 0;
	char filename[FNSIZE];	        //储存输入的文件名

	printf("输入需要打开的文件名:");
	while (scanf_s("%s", filename, FNSIZE) != 1)
	{
		printf("那不是一个有效的文件名,请输入正确的文件名:");
		continue;		//处理错误输入
	}
	if ((err = fopen_s(&fp, filename, "r")) != 0)
	{
		printf("打开 %s 文件失败, 退出中\n", filename);
		exit(EXIT_FAILURE);	//打开文件失败
	}
	while ((ch = getc(fp)) != EOF)
	{
		putc(ch, stdout);	// 与 putchar(ch); 相同
		count++;
	}
	fclose(fp);			//关闭文件
	printf("\n\n文件 %s 有 %lu 个字符\n", filename, count);

	system("pause");
	return 0;
}

这里的49 个字符是包括换行符的(不包括文件结尾)

Pe13-2:

这里我(终于)使用了命令行参数

VS使用命令行参数的方法:       任意门

/*
    13-2----编写一个文件拷贝程序,该程序通过命令行获取原始文件名。
    尽量使用标准I/O 和二进制模式。
*/

#include <stdio.h>
#include <stdlib.h>	    //提供 exit() 的原型


//拷贝文件函数(传递源文件名和目标文件名)
void cpyfile(const char * source, const char * target);
	
int main(int argc, char * argv[])               //使用命令行参数
{
	int ch;

	if (argc != 3)				//命令行参数接收错误
	{
		puts("需要两个命令行参数,程序退出中......\n");
		exit(EXIT_FAILURE);
	}
	cpyfile(argv[1], argv[2]);	        //拷贝文件函数
	puts("文件拷贝完毕!\n");

	while (ch = getchar())		        //防止突然退出
	{
		continue;
	}
	return 0;
}

//拷贝文件函数
void cpyfile(const char * source, const char * target)
{
	errno_t err;	    //fopen_s() 函数返回该类型(int)
	FILE * fp1;
	FILE * fp2;
	int ch = 0;
	
	if (err = fopen_s(&fp1, source, "rb") != 0)
	{
		printf("打开 %s 文件失败,退出中......\n", source);
		exit(EXIT_FAILURE);
	}
	if (err = fopen_s(&fp2, target, "ab+") != 0)
	{
		printf("打开 %s 文件失败,退出中......\n", target);

		/*因为先前打开源文件过了,所以需要关闭*/
		if (fclose(fp1) != 0)	        //关闭源文件失败, 关闭成功返回0,否则EOF
		{
			printf("关闭 %s 文件失败,退出中......\n", source);
		}
		exit(EXIT_FAILURE);		//只需要一个exit() 想想为什么
	}

	/* 成功打开两个文件后 */

	while ((ch = fgetc(fp1)) != EOF)
	{
		fputc(ch, fp2);			

	    /*
		fgetc:
		int fgetc (FILE * stream );
	        返回指定流的内部文件位置指示器当前指向的字符。
                然后将内部文件位置指示器推进到下一个字符。
		如果在调用时流位于文件末尾, 则该函数返回 EOF 并设置流的文件结尾指示器 (feof).

		fputc:
		int fputc (int character, FILE * stream );
		将字符写入流并前进位置指示器。
		字符被写在由流的内部位置指示器指示的位置, 然后由一个自动推进.

		以上是MSDN的资料

		使用这两个函数比使用 fread() 和 fwrite() 更简便
	    */
	}

	//关闭文件
	if (fclose(fp1) != 0)
	{
		printf("关闭 %s 文件失败,退出中......\n", source);
		exit(EXIT_FAILURE);
	}
	if (fclose(fp2) != 0)
	{
		printf("关闭 %s 文件失败,退出中......\n", target);
		exit(EXIT_FAILURE);
	}

	return;
}

这里我使用到了 fgetc()fputc() 两个函数 代替 fread()fwrite() 函数

如果注释看不清,可以在 函数原型 里找到这两个函数。

命令行参数:

源文件:

目标文件:

程序执行:

目标文件:

Pe13-3:

/*
    13-3----编写一个文件拷贝程序,提示用户输入文本文件名,
    并以该文件作为原始文件名和输出文件名。
    改成需要使用 ctype.h 中的 toupper () 函数,
    再写入到输入文件时把所有文本转换成大写。
    使用标准I/O 和文本模式。
*/

呃,原谅我真的搞不懂这个题目是什么意思...... 多线程?玩不来》。。。。

只好自己建立一个 infomation.txt 了

As the source file !!! 

#include <stdio.h>
#include <stdlib.h>		    //提供 exit() 的原型
#include <ctype.h>		    //toupper() 函数
#define TEMPNAME "infomation.txt"

//拷贝文件函数(并转换成大写)
void cpyfile(FILE * source, FILE * target);
	
int main(void)
{
	int ch;
	char fname[256];
	errno_t err;			//fopen_s() 函数返回该类型(int)
	FILE * fp1;			//源文件
	FILE * fp2;			//目标文件

	puts("输入目标文件名:");
	gets_s(fname, 256);

	/* 打开文件(失败) */
	if (err = fopen_s(&fp1, TEMPNAME, "r") != 0)
	{
		printf("打开 %s 文件失败,程序退出中......\n", TEMPNAME);
		exit(EXIT_FAILURE);
	}

	if (err = fopen_s(&fp2, fname, "a+") != 0)
	{
		printf("打开 %s 文件失败,程序退出中......\n", fname);
		if (fclose(fp1) != 0)
			printf("关闭 %s 文件失败,程序退出中......\n", TEMPNAME);

		exit(EXIT_FAILURE);
	}

	cpyfile(fp1, fp2);		//拷贝文件函数((并转换成大写))
	puts("文件拷贝完毕!\n");

	/* 关闭文件(失败) */
	if (fclose(fp1) != 0)
	{
		printf("关闭 %s 文件失败,程序退出中......\n", TEMPNAME);
		exit(EXIT_FAILURE);
	}
	if (fclose(fp2) != 0)
	{
		printf("关闭 %s 文件失败,程序退出中......\n", fname);
		exit(EXIT_FAILURE);
	}

	while (ch = getchar())		//防止突然退出
	{
		continue;
	}
	return 0;
}

//拷贝文件函数(并转换成大写)
void cpyfile(FILE * source, FILE * target)
{
	int ch = 0;

	while ((ch = fgetc(source)) != EOF)
	{
		ch = toupper(ch);
		fputc(ch, target);			
	}

	return;
}

运行效果:::::

Pe13-4:

/*
    13-4----编写一个程序,按顺序在屏幕上显示命令行中列出的所有文件。
    使用argc控制循环
*/

#include <stdio.h>
	
int main(int argc, char *argv[])
{
	int ch;
	int ct;
	errno_t err;			//fopen_s()返回该类型(int)
	FILE * fp; 
	char file[4096];

	for (ct = 1; ct < argc; ct++)
	{
		//打开文件失败
		if (err = fopen_s(&fp, argv[ct], "r") != 0)
		{
			printf("打开 %s 文件失败...\n", argv[ct]);
			continue;	//注意for () 使用continue ct 会递增
		}

		printf("%d) 文件 %s : \n\n", ct, argv[ct]);
		while (fgets(file, 256, fp) != NULL)
		{
			fputs(file, stdout);
		}
		printf("\n\n");
		fclose(fp);		//别忘了关闭文件呦
	}

	printf("\n\n程序完结!\n");
	while (ch = getchar())		//防止突然退出
	{
		continue;
	}

	return 0;
}

运行:

Pe13-5:

/*
    13-5----修改程序清单 13-5 中的程序,用命令界面代替交互式界面.
*/

这里我传递的命令格式:[目标文件] [源文件1] [源文件2] ......(以此类推)

总共3个命令参数

文件:

源码:

#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;		//附加的文件数量
	/* argv[1] 表示源文件, argv[2] 表示目标文件 */
	int ch;
	errno_t err;		
	
	/* 打开目标文件 */
	if ((err = fopen_s(&fa, argv[1], "a+")) != 0)
	{
		fprintf(stderr, "Can't open %s\n", argv[2]);
		exit(EXIT_FAILURE);
	}
	//设置目标文件的缓冲区
	if (setvbuf(fa, NULL, _IOFBF, BUFSIZE) != 0)
	{
		fputs("Can't create output buffer\n", stderr);
		if (fclose(fa) != 0)
			printf("Can't close %s\n", argv[2]);
		exit(EXIT_FAILURE);
	}

	for (int ct = 2; ct < argc; ct++)
	{
		//比较字符串(如果相同,则不拷贝)       且 源字符串不是空文件
		if (strcmp(argv[1], argv[ct]) && *argv[ct] == '\0')
		{
			fputs("Can't append file itself\n", stderr);
		}
		//打开源文件
		else if ((err = fopen_s(&fs, argv[ct], "r")) != 0)
		{
			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[ct]);
			if (ferror(fa) != 0)
				fprintf(stderr, "Error in reading file %s.\n", argv[1]);
			
                        fclose(fs);
			files++;
			printf("File %s appended.\n", argv[ct]);
		}
	}

	printf("Done appending. %d files appended.\n\n", files);
	rewind(fa);				//回到文件开始处
	printf("%s contents:\n", argv[1]);

	//打印目标文件的内容
	while ((ch = fgetc(fa)) != EOF)		//这里我用了 fgetc() 而不是getc()
	{
		putchar(ch);			//fgetc() 是函数     getc() 是宏定义
	}	                                //资料自行百度									            
	puts("\n\nDone displaying.");			
	fclose(fa);

	return 0;
}

void append(FILE * source, FILE * dest)
{
	size_t bytes;
	static char temp[BUFSIZE]; //只分配一次
	
	while ((bytes = fread_s(temp, sizeof(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)
		{
			*find = 0;
		}
		else
		{
			while (getchar() != '\n')
				continue;
		}
	}

	return ret_val;
}

运行:

Pe13-6:

/*
    13-6-----重写程序清单 13.2 中的程序,不使用命令行参数,而是提示用户输入所需的信息。
*/

#include <stdio.h>
#include <stdlib.h>		//提供 exit() 的原型
#include <string.h>		//提供 strcpy(),strcat()的原型
#define LEN 40

int main(void)
{
	FILE * in, * out;
	errno_t err;
	int ch;
	int ct = 0;			//count 的缩写
	char Tname[LEN] = { '\0' };	//目标文件名
	char Sname[LEN] = { '\0' };	//源文件名

	printf("Enter the name of source file:");	
	do
	{                               //使用 do while 很舒服!
		gets_s(Sname, LEN);	//如果源文件名为空,则重新输入

	} while (Sname[0] == '\0');

	/* 打开源文件 */
	if ((err = fopen_s(&in, Sname, "r")) != 0)
	{
		fprintf(stderr, "Can't open %s file", Sname);
		exit(EXIT_FAILURE);
	}
	//设置输出
	strncpy_s(Tname, LEN, Sname, LEN - 5);
	Tname[LEN - 5] = '\0';
	
	/* 在文件名后添加 .red */
	strcat_s(Tname, sizeof(Tname), ".red");

	//打开目标文件
	if ((err = fopen_s(&out, Tname, "w")) != 0)
	{
		fprintf(stderr, "Can't create output file.\n");
		exit(3);
	}

	//拷贝数据
	while ((ch = fgetc(in)) != EOF)
	{
		if (ct++ % 3 == 0)
		{
			fputc(ch, out);
		}
	}
	//收尾工作
	if (fclose(in) != 0 || fclose(out) != 0)
	{
		fprintf(stderr, "Error in closing files\n");
	}

	return 0;
}

Pe13-7:

/*
    13-7------编写一个程序打开两个文件。
    可以使用命令行参数或提示用户输入人文件名。
    a.    该程序以这样的顺序打印:打印第 1 个文件的第 1 行,第 2 个文件的第 2 行,
        第 1 个文件的第 2 行,第 2 个文件的第 2 行,以此类推,打印到行数较多文件的最后一行

    b.    修改该程序,把行号相同的行打印成一行
*/

注意这里的 rewind() 函数,我写在函数原型里了

#include <stdio.h>
#include <stdlib.h>				
#include <string.h>				
#define BUFSIZE     256
#define LEN	    81

int Pe13_7_a(FILE * fp1, FILE * fp2);
int Pe13_7_b(FILE * fp1, FILE * fp2);
char * Getfname(char * fname, int Maxlen);	//获取文件名

int main(void)
{
	errno_t err;
	FILE * fp1, *fp2;			//两个文件指针
	char fname1[LEN] = {'\0'};		//文件名1, 初始化为空
	char fname2[LEN] = {'\0'};		//文件名2, 初始化为空
	
	printf("Enter the first file name: ");	//获取文件名1
	Getfname(fname1, LEN);
	printf("Enter the second file name: ");	//获取文件名2
	Getfname(fname2, LEN);

	/* 打开两个文件 */
	if ((err = fopen_s(&fp1, fname1, "r")) != 0)
	{
		fprintf(stderr, "Can't open the file: %s \n", fname1);
		exit(EXIT_FAILURE);
	}
	if ((err = fopen_s(&fp2, fname2, "r")) != 0)
	{
		fprintf(stderr, "Can't open the file: %s \n", fname2);
		fclose(fp1);
		exit(EXIT_FAILURE);
	}
	Pe13_7_a(fp1, fp2);			//函数调用
	Pe13_7_b(fp1, fp2);			//函数调用

	//收尾工作
	if ((fclose(fp1) != 0) || (fclose(fp2) != 0))
	{
		fprintf(stderr, "Something wrongs in closing the files.\n");
	}

	return 0;
}


int Pe13_7_a(FILE * fp1, FILE * fp2)
{
	char buf[BUFSIZE];			//创建缓冲区
	int ct = 1,
		time = 1;			//标记行号
	int flag1 = 1,
		flag2 = 1;			//标记文件是否结束
	int ret_val = 1;		        //返回值默认为1,返回0 表示成功调用函数
	
	printf("Here are the 'a'(one line by one line): \n");
	while (flag1 != 0 || flag2 != 0)
	{
		if (flag1 != 0)			//分别打印文件1 文件2 的内容
		{
			printf("File 1: Line %d: ", ct++);
			if ((fgets(buf, BUFSIZE, fp1) != NULL))				
			{
				fputs(buf, stdout);
			}
			else
			{
				printf("\n\tThe first file has done.\n");
				flag1 = 0;
			}
		}
		if (flag2 != 0)
		{
			printf("File 2: Line %d: ", time++);
			if ((fgets(buf, BUFSIZE, fp2) != NULL))
			{
				fputs(buf, stdout);
			}
			else
			{
				printf("\n\tThe second file has done.\n");
				flag2 = 0;
			}
		}
	}
	rewind(fp1);				//回到文件开头
	rewind(fp2);				//如果没有,则无法读取文件
	printf("\nthe task a: Done!\n\n");
	return 0;
}

int Pe13_7_b(FILE * fp1, FILE * fp2)
{
	char buf[BUFSIZE];			//创建缓冲区
	int ct = 1;
	int flag1 = 1,
		flag2 = 1;			//标记文件是否结束
	int ret_val = 1;			//返回值默认为1,返回0 表示成功调用函数
	char * find;				//使用strchr() 函数

	printf("Here are the 'b'(make for the whole line): \n");
	while (flag1 != 0 || flag2 != 0)
	{
		printf("The whole line %d: ", ct++);
		if (flag1 != 0)
		{
			if ((fgets(buf, BUFSIZE, fp1) != NULL))
			{
				find = strchr(buf, '\n');	//查找换行符
				if (find != NULL)			
				{
					*find = '\0';		//删去换行符
				}
				fputs(buf, stdout);		//打印一行
			}
			else
			{
				printf("(The first file has done.)");
				flag1 = 0;
			}
		}
		if (flag2 != 0)
		{
			if ((fgets(buf, BUFSIZE, fp2) != NULL))
			{
				find = strchr(buf, '\n');	//查找换行符
				if (find != NULL)
				{
					*find = '\0';		//删去换行符
				}
				fputs(buf, stdout);		//打印一行
			}
			else
			{
				printf("(The second file has done.)");
				flag2 = 0;
			}
		}
		putchar('\n');			//每次打印结束后,插入换行符
	}

	printf("\nthe task b: Done!\n\n");
	return 0;
}

char * Getfname(char * fname, int Maxlen)	//获取文件名
{
	do
	{
		gets_s(fname, Maxlen);
	} while (fname[0] == '\0');		//如果是空行输入, 则重新输入

	return fname;
}

源文件:

运行:

剩下的继续写.....

会陆续发出来的

Alex Mercer (boy) 鸣谢!

猜你喜欢

转载自blog.csdn.net/Alex_mercer_boy/article/details/81978835