《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
代码可以直接复制编译
对了,如果各位大佬们在编译代码时出现一些奇奇怪怪的错误,比如未定义或未声明,
可以看这个教程 教程连接 ← 如果出现了某些错误,可以看看这篇文章!感谢!
_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():
int fgetc (FILE * stream );
fgetc():
返回指定流的内部文件位置指示器当前指向的字符。然后将内部文件位置指示器推进到下一个字符。
如果在调用时流位于文件末尾, 则该函数返回 EOF 并设置流的文件结尾指示器 (feof). (转至MSDN)
fputc():
int fputc (int character, FILE * stream );
fputc():
将字符写入流并前进位置指示器。
字符被写在由流的内部位置指示器指示的位置, 然后由一个自动推进. (转至MSDN)
rewind() (C库函数)功能:
将文件内部的指针重新指向一个流的开头
注意:不是文件指针而是文件内部的位置指针,
随着对文件的读写文件的位置指针(指向当前读写字节)向后移动。
而文件指针是指向整个文件,如果不重新赋值文件指针不会改变。
如果需要的,可以参考下面的 Pe13-7 练习题 或自行百度
/*
13-1 ----修改程序清单 13.1 中的程序,
要求提示用户输入文件名,并读取用户输入的信息,不使用命令行参数。*/
#include
#include //提供 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 个字符是包括换行符的(不包括文件结尾)
这里我(终于)使用了命令行参数
VS使用命令行参数的方法: 任意门
/*
13-2----编写一个文件拷贝程序,该程序通过命令行获取原始文件名。
尽量使用标准I/O 和二进制模式。
*/
#include
#include //提供 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() 函数
如果注释看不清,可以在 函数原型 里找到这两个函数。
命令行参数:
源文件:
目标文件:
程序执行:
目标文件:
/*
13-3----编写一个文件拷贝程序,提示用户输入文本文件名,
并以该文件作为原始文件名和输出文件名。
改成需要使用 ctype.h 中的 toupper () 函数,
再写入到输入文件时把所有文本转换成大写。
使用标准I/O 和文本模式。
*/
呃,原谅我真的搞不懂这个题目是什么意思...... 多线程?玩不来》。。。。
只好自己建立一个 infomation.txt 了
As the source file !!!
#include
#include //提供 exit() 的原型
#include //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;
}
运行效果:::::
/*
13-4----编写一个程序,按顺序在屏幕上显示命令行中列出的所有文件。
使用argc控制循环
*/
#include
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;
}
运行:
/*
13-5----修改程序清单 13-5 中的程序,用命令界面代替交互式界面.
*/
这里我传递的命令格式:[目标文件] [源文件1] [源文件2] ......(以此类推)
总共3个命令参数
文件:
源码:
#include
#include
#include
#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;
}
运行:
/*
13-6-----重写程序清单 13.2 中的程序,不使用命令行参数,而是提示用户输入所需的信息。
*/
#include
#include //提供 exit() 的原型
#include //提供 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;
}
/*
13-7------编写一个程序打开两个文件。
可以使用命令行参数或提示用户输入人文件名。
a. 该程序以这样的顺序打印:打印第 1 个文件的第 1 行,第 2 个文件的第 2 行,
第 1 个文件的第 2 行,第 2 个文件的第 2 行,以此类推,打印到行数较多文件的最后一行b. 修改该程序,把行号相同的行打印成一行
*/
注意这里的 rewind() 函数,我写在函数原型里了
#include
#include
#include
#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;
}
源文件:
运行:
/*
13-8------编写一个程序,以一个字符和任意文件名作为命令行参数。
如果字符后面没有参数,该程序读取标准输入;否则,程序依次打开每个文件
并报告每个文件中该字符出现的次数。文件名和字符本身也要一同报告。
程序应包含错误检查,已确定参数数量是否正确和是否能打开文件。如果无法打开文件,
程序应该报告这一情况,然后继续处理下一个文件。
*/
#include
#include
//在缓存区内查找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;
}
/*
13-9------修改程序清单 13.3 中的程序,从 1 开始, 根据加入列表的顺序为每个单词编号。
当长序下次运行时 ,确保新的单词编号接着上次的编号开始。
*/
#include
#include
#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;
}
/*
13-10------编写一个程序打开一个文本文件,通过交互方式获取文件名。通过一个循环,
提示用户输入一个文件位置。然后该程序打印从该位置开始到下一个换行符之前的内容,
用户输入负值或非数值字符可以结束循环。
*/
行云流水般一气呵成!
#include
#include
#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;
}
/*
13-11------编写一个程序,接收两个命令行参数。第 1 个参数是一个字符串,第2个参数是一个
文件名。然后该程序查找该文件,打印文件中包含该字符串的所有行。因为该任务是面向行而不是面向
字符的,所有要使用 fgets() 而不是 getc()。使用标准 C 库函数 strstr(),
在每一行中查找指定字符串。假定文件中的所有行都不超过255个字符。
*/
#include
#include
#include
#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 ......噗哈哈
/*
13-12-----创建一个文本文件,内含20 行, 每行30 个整数。这些证书都在0~9 之间,
用空格分开。该文件是用数字表示一张图片,0~9表示逐渐增加的灰度。
呸,这么长的题目.......好吧,各位大佬自己看书上哈。
*/
程序中需要使用到的文本文件:
链接:https://pan.baidu.com/s/1Kmj2FJoaxQYcHp5EYqWXtQ 密码:tula
#include
#include
#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不支持变长数组
许久未见了,继续发:
/*
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了。
第十四章的内容估计不会很快出来,跟大家道句遗憾。
因为博主我自己也观察了一番,
1.这样一个练习一个练习的发,不说它效果慢吧,而且对于有需要的人而言,等得很急!
2.博主本人也是个学生,暑假结束了,时间也真的不多,一周最多就是1个编程练习,甚至没有这么多。
3.粉丝和浏览数也涨得慢(嘿嘿,主要原因)
所以我打算,等我学完第十四章后,用可能1,2个月的时间完成所有的练习,再一口气发出来。
很舒服! 但,读者们可能需要等到10或11月才会有下一个博客出来,时间会很长,很久......
好啦,好在博主是打算坚持写博客的,决不放弃!
OK,咱们,第14章再见吧!
Alex Mercer (boy) 鸣谢!