《C Primer Plus》第6版 編程練習答案參考 第十三章(全)

《C Primer Plus》第6版 編程練習 第十三章


目錄(如果無法使用,可以選用右側的目錄選項)教程連接

先看這裏(很重要!):

函數原型:

    fopen_()   :

    fgetc() 和 fputc():

    rewind() 函數:

源碼 + 題目 + 運行效果:

Pe13-1:

Pe13-2:

Pe13-3:

Pe13-4:

Pe13-5:

Pe13-6:

Pe13-7:

Pe13-8:

Pe13-9:

Pe13-10:

Pe13-11:

Pe13-12:

P13-14:

關於第14章的,這裏先說:



先看這裏(很重要!):

本書中所有使用的 fpoen() 函數由於 VS2017 提示不安全

所以全部使用 fopen_s() 函數代替,函數原型 下面會一一展示出來。

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

編譯環境 : VS 2017 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;
}

源文件:

運行:

Pe13-8:

/*
    13-8------編寫一個程序,以一個字符和任意文件名作爲命令行參數。
    如果字符後面沒有參數,該程序讀取標準輸入;否則,程序依次打開每個文件
    並報告每個文件中該字符出現的次數。文件名和字符本身也要一同報告。
    程序應包含錯誤檢查,已確定參數數量是否正確和是否能打開文件。如果無法打開文件,
    程序應該報告這一情況,然後繼續處理下一個文件。
*/

#include <stdio.h>
#include <stdlib.h>							

//在緩存區內查找ch,返回ch出現的次數
int ch_in_buf(const char * buf, int ch);

int main(int argc, char * argv[])
{
	int ch;
	FILE * fp;
	errno_t err;
	int ct = 0;			//儲存字符出現的次數
	int time;
	char buf[256] = {'\0'};

	if (argc < 2)
	{
		fprintf(stderr, "Usage: [character] [filename]......\n");
		exit(EXIT_FAILURE);
	}
	else if (argc == 2)		//命令行參數爲一個字符
	{
		ch = argv[1][0];	//獲取傳入的字符

		printf("Enter some text, press the # key at"
			"the beginning of a line to terminate.\n");
		while ((fgets(buf, 256, stdin)) != NULL && buf[0] != '#')
		{
			ct += ch_in_buf(buf, ch);
			printf("Next line: \n");
		}
		printf("The character %c has appeared %d times"
                        "in your input.", ch, ct);
	}
	else
	{
		ch = argv[1][0];	//獲取傳入的字符
		//分別打開每個文件
		for (time = 2; time < argc; time++)
		{
			if ((err = fopen_s(&fp, argv[time], "r")) != 0)
			{
				fprintf(stderr, "Can't open file %s.\n", argv[time]);
				continue;
			}
			//查找字符
			while ((fgets(buf, 256, fp)) != NULL)
			{
				ct += ch_in_buf(buf, ch);
			}
			printf("The character %c has appeared %d times"
                                 "in file %s\n" ,   ch, ct, argv[time]);
			fclose(fp);
		}
	}
	
	printf("BYE!\n");
	return 0;
}

//在緩存區內查找ch,返回ch出現的次數
int ch_in_buf(const char * buf, int ch)
{
	int count = 0;

	while (*buf)
	{
		if (*buf == ch)
		{
			count++;
		}
		buf++;
	}
	return count;
}

 

Pe13-9:

/*
    13-9------修改程序清單 13.3 中的程序,從 1 開始, 根據加入列表的順序爲每個單詞編號。
    當長序下次運行時 ,確保新的單詞編號接着上次的編號開始。
*/

#include <stdio.h>
#include <stdlib.h>							
#define MAX 41

int main(void)
{
	FILE * fp;
	errno_t err;
	char words[MAX] = { '\0' };
	int ct = 1;

	//1.打開文件
	if ((err = fopen_s(&fp, "text.txt", "a+")) != 0)
	{
		fprintf_s(stderr, "Can't open \"text.txt\" file.\n");
		exit(EXIT_FAILURE);
	}

	//2.提示輸入
	puts("Enter words to add to the file; press the #");
	puts("key at the beginning of a line to terminate.\n");

	//3.獲取輸入 並寫入文件
	while ((fscanf_s(stdin, "%40s", words, 40)) == 1 && (words[0] != '#'))
		fprintf(fp, "%s\n", words);

	//4.打印文件內的所有單詞
	puts("File contents:");
	rewind(fp);		//回到文件結尾

	while (fscanf_s(fp, "%s", words, 40) == 1)
	{
		printf("%d) %s\n", ct, words);
		ct++;
	}
	puts("Done!");
	
	//5.關閉文件
	if (fclose(fp) != 0)
	{
		fprintf(stderr, "Error closing file.\n");
	}

	return 0;

}

 

Pe13-10:

/*
    13-10------編寫一個程序打開一個文本文件,通過交互方式獲取文件名。通過一個循環,
    提示用戶輸入一個文件位置。然後該程序打印從該位置開始到下一個換行符之前的內容,
    用戶輸入負值或非數值字符可以結束循環。
*/

行雲流水般一氣呵成! 

#include <stdio.h>
#include <stdlib.h>							
#define BUFSIZE 256

int main(void)
{
	FILE * fp;				//文件指針
	errno_t err;				//fopen_s()返回該類型(int)
	char buf[BUFSIZE] = { '\0' };
	char fname[41] = { '\0' };
	int num = 0;				//用戶指定的文件位置
	long last;				//fseek()函數和ftell()函數使用

	/* 這裏我使用了fseek()函數 和 ftell()函數,函數作用和原型書上有 */

	//1.獲取文件名
	printf("Enter the name of the file to open:");
	while (gets_s(fname, 40) == NULL)
		continue;

	//2.打開文件
	if ((err = fopen_s(&fp, fname, "r")) != 0)
	{
		fprintf_s(stderr, "Can't open file %s.\n", fname);
		exit(EXIT_FAILURE);
	}

	//3.獲取文件長度(從文件開頭到文件結尾的字符數)
	fseek(fp, 0L, SEEK_END);
	last = ftell(fp);

	//4.獲取用戶輸入的文件位置
	printf("Enter the location of the file to query(q to quit): ");
	while ((scanf_s("%d", &num) == 1) && num >= 0)
	{
		if (num > last)			//超出文本字符數
		{
			fprintf_s(stderr, "%d exceeds the number of"
				"text characters.\nEnter one again?\n:", num);
			continue;
		}
		//5.定位文件並獲取文件內容
		fseek(fp, (long)num, SEEK_SET);
		fgets(buf, BUFSIZE, fp);
		puts("The text in that line:");
		fputs(buf, stdout);
		rewind(fp);			//回到文件開頭
		printf("Enter the location of the file to query(q to quit): ");
	}

	//6.收尾工作
	fclose(fp);

	return 0;
}

Pe13-11:

/*
    13-11------編寫一個程序,接收兩個命令行參數。第 1 個參數是一個字符串,第2個參數是一個
    文件名。然後該程序查找該文件,打印文件中包含該字符串的所有行。因爲該任務是面向行而不是面向
    字符的,所有要使用 fgets() 而不是 getc()。使用標準 C 庫函數 strstr(),
    在每一行中查找指定字符串。假定文件中的所有行都不超過255個字符。
*/

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

//查找並打印包含字符串 st 文件行
char * ChrStrIn_Buf(const char * buf, const char * st);

int main(int argc, char * argv[])
{
	FILE * fp;
	errno_t err;
	char buf[BUFSIZE] = { '\0' };
	int flag = 0;

	//檢查參數
	if (argc != 3)
	{
		fprintf_s(stderr, "Usage:[string] [file name].\n");
		exit(EXIT_FAILURE);
	}
	//打開文件
	if ((err = fopen_s(&fp, argv[2], "r")) != 0)
	{
		fprintf_s(stderr, "Can't open file %s.\n", argv[2]);
		exit(EXIT_FAILURE);
	}
	//查找字符串
	while (fgets(buf, BUFSIZE, fp) != NULL)
	{
		if (ChrStrIn_Buf(buf, argv[1]) != NULL)
			flag = 1;
	}
	if (flag == 0)
	{
		printf("Didn't find %s in file %s.\n", argv[1], argv[2]);
	}

	//收尾工作
	fclose(fp);
	return 0;
}

//查找並打印包含字符串 st 文件行
char * ChrStrIn_Buf(const char * buf, const char * st)
{
	char *find;

	//查找字符串
	if ((find = strstr(buf, st)) != NULL)
	{
		printf("Find %s in:\n", st);
		fputs(buf, stdout);
		printf("\nThat is:\n");
		fputs(find, stdout);
	}

	return find;
}

別問我爲什麼那麼喜歡用 10086 ......噗哈哈

Pe13-12:

/*
    13-12-----創建一個文本文件,內含20 行, 每行30 個整數。這些證書都在0~9 之間,
    用空格分開。該文件是用數字表示一張圖片,0~9表示逐漸增加的灰度。
    呸,這麼長的題目.......好吧,各位大佬自己看書上哈。
*/

程序中需要使用到的文本文件:

鏈接:https://pan.baidu.com/s/1Kmj2FJoaxQYcHp5EYqWXtQ 密碼:tula

#include <stdio.h>
#include <stdlib.h>							
#define ROWS 20
#define INTLENTH 30
#define FNAME "text.txt"		//這裏爲了方便,我直接設置了:
#define OUTFILE "out.txt"		//文本文件名和輸出文件名

//從文件中獲取數字int數組
void GetStringFromFile(FILE * fp, int arr[][INTLENTH], int rows);

int main(void)
{
	FILE * fp1, *fp2;	        //fp1 表示源文件,fp2表示輸出文件
	errno_t err;
	int arr[ROWS][INTLENTH] = { 0 };
	int time, ct;

	//打開源文件
	if ((err = fopen_s(&fp1, FNAME, "r")) != 0)
	{
		fprintf_s(stderr, "Can't open file %s.\n", FNAME);
		exit(EXIT_FAILURE);
	}
	//打開輸入文件
	if ((err = fopen_s(&fp2, OUTFILE, "w+")) != 0)
	{
		fprintf_s(stderr, "Can't open file %s.\n", OUTFILE);
		fclose(fp1);
		exit(EXIT_FAILURE);
	}

	//從文件中獲取數字int數組
	GetStringFromFile(fp1, arr, ROWS);

	printf("Here is the file:\n\n\n");
	//打印結果並寫入文件
	for (time = 0; time < ROWS; time++)
	{
		for (ct = 0; ct < INTLENTH; ct++)
		{
			switch (arr[time][ct])
			{
			case 0:
				putchar(' ');
				fputc(' ', fp2);
				break;
			case 1:
				putchar('.');
				fputc('.', fp2);
				break;
			case 2:
				putchar('\'');		//輸出 ' 要使用 \'
				fputc('\'', fp2);
				break;
			case 3:
				putchar(':');
				fputc(':', fp2);
				break;
			case 4:
				putchar('~');
				fputc('~', fp2);
				break;
			case 5:
				putchar('*');
				fputc('*', fp2);
				break;
			case 6:
				putchar('=');
				fputc('=', fp2);
				break;
			case 7:
				putchar('$');		//這個 $ 是我自己選的
				fputc('$', fp2);
				break;
			case 8:
				putchar('%');
				fputc('%', fp2);
				break;
			case 9:
				putchar('#');
				fputc('#', fp2);
				break;
			default:
				break;
			}
		}
		fputc('\n', fp2);
		fputc('\0', fp2);
		printf("\n");
	}
	return 0;
}

//從文件中獲取數字int數組
void GetStringFromFile(FILE * fp, int arr[][INTLENTH], int rows)
{
	int ct, time;

	for (time = 0; time < rows; time++)
	{
		for(ct = 0; ct < INTLENTH; ct++)
		{
			//獲取文件中的每一個(數字字符)
			//使用scanf_s()函數會自動將char類型字符轉換成int類型
			fscanf_s(fp, "%d", &arr[time][ct]);
		}
	}
}

運行:

P13-13:假的!VS不支持變長數組

P13-14:

許久未見了,繼續發:

/*
    13-14-----數字圖像,尤其是從宇宙飛船發回的數字圖像,可能會包含一些失真。爲編程練習12添加消除失真的函數。

    該函數把每個值與它上下左右相鄰的值作比較,如果該值與其周圍相鄰值的差都大於1,

    則用所有相鄰值的平均值(四捨五入爲整數)代替該值。

    注意,與邊界上的點相鄰的點小於 4 個,所以做特殊處理。

*/

因爲如果要再重新把所有代碼都發出來會很麻煩,而且看着很不舒服,

所以這裏直接寫了一個函數來完成這個步驟:

只需要在 GetSringFromFile()函數後面調用即可,像下面那樣

	//從文件中獲取數字int數組
	GetStringFromFile(fp1, arr, ROWS);
	//處理數字圖像的失真
	DealDistortion(arr, ROWS);
void DealDistortion(int image[][INTLENTH], int rows)
{
    int time,  ct;		//循環計數變量

    //遍歷所有元素
    for (time = 0; time < rows; time++)
    {
        for (ct = 0; ct < INTLENTH; ct++)
       	{			
	    int count = 0;		//記錄已經遍歷過幾個方向
            int number = 0;		//儲存每次獲取到的各個方向的值
	    int sum = 0;		//儲存相鄰值的點數和
		
            //儲存inamg[time][ct]的值
	    int localnum = image[time][ct];

	    //標記相鄰值的差值,如果大於1,該值爲1,反之,該值爲0
            //如果某方向沒有數據,也標記爲1
	    int flag[4] = { 0,0,0,0 };

	    //值的上方有數據
	    if (time != 0)
	    {
		number = image[time - 1][ct];    //獲取上方的值
       	        //比較上方的值和原值
	        if (number - localnum > 1 || number - localnum < -1)
	        {
		    sum += number;
		    flag[0] = 1;	//差值大於1
		    count++;		//記錄1個方向
		}
	    }
	    else
                flag[0] = 1;		//如果上方沒有數據

	    //值的左方有數據
	    if (ct != 0)
	    {
		number = image[time][ct - 1];    //獲取左方的值
	        if (number - localnum > 1 || number - localnum < -1)
		{
		    sum += number;
		    flag[1] = 1;		//差值大於1
		    count++;		//記錄1個方向
		}
	    }
	    else
		flag[1] = 1;		//如果左方沒有數據

	    //值的右方有數據
	    if (time != 19)
	    {
		number = image[time][ct + 1];    //獲取右方的值
		if (number - localnum > 1 || number - localnum < -1)
		{
		    sum += number;
		    flag[2] = 1;		//差值大於1
		    count++;		//記錄1個方向
		}
	    }
	    else
		flag[2] = 1;		//如果右方沒有數據

	    //值的下方有數據
	    if (ct != 29)
	    {
		number = image[time + 1][ct];    //獲取下方的值
	    	if (number - localnum > 1 || number - localnum < -1)
		{
		    sum += number;
		    flag[3] = 1;		//差值大於1
		    count++;		//記錄1個方向
		}
	    }
	    else
	        flag[3] = 1;		        //如果下方沒有數據

	    //四周的差值都大於1,刷新原值。只要有1個差值小於1,則不刷新
	    if (flag[0] != 1 || flag[1] != 1 || flag[2] != 1 || flag[3] != 1)
	    {
		flag[0] = 2;		//標記不刷新
	    }

	    //刷新原值,注意,最後的除數不能爲0
	    if (flag[0] != 2 && count != 0)
	    {
		image[time][ct] = (int)(sum / count);
	    }
	}
    }
}

來看看效果哈!

不過要我說,命令行下的好看多了,更簡潔大方!

(吐槽記事本......)

OK!到這裏,整個第13章就over了。

關於第14章的,這裏先說:

第十四章的內容估計不會很快出來,跟大家道句遺憾。

因爲博主我自己也觀察了一番,

1.這樣一個練習一個練習的發,不說它效果慢吧,而且對於有需要的人而言,等得很急!

2.博主本人也是個學生,暑假結束了,時間也真的不多,一週最多就是1個編程練習,甚至沒有這麼多。

3.粉絲和瀏覽數也漲得慢(嘿嘿,主要原因)

所以我打算,等我學完第十四章後,用可能1,2個月的時間完成所有的練習,再一口氣發出來。

很舒服!      但,讀者們可能需要等到10或11月纔會有下一個博客出來,時間會很長,很久......

好啦,好在博主是打算堅持寫博客的,決不放棄!

OK,咱們,第14章再見吧!

 

Alex Mercer (boy) 鳴謝!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章