利用标准I/O函数,实现两个文件的复制功能
主要使用函数原型:
1.每次读写一个字符:
int fgetc(FILE *stream);
int fputc(int c, FILE *stream);
2.每次读写一行字符:
char *fgets(char *s , int size , FILE *stream);
char *fputs(const char *s ,FILE *stream);
3.每次读写一个块字符:
size_t fread(void *ptr,size_t size,xize_t nmemb,FILE\*stream);
size_t fwrite(const void *ptr,size_t size,xize_t nmemb,FILE\*stream);
4.另外,每个函数也使用了一些辅助函数,主要列举如下:
int feof(FILE *stream);
int ferror(FILE *stream);
long ftell(FILE *stream); //获取当前的位置偏移量
实现过程中几个易错细节小结
-
几个读取函数的文件指针偏移
int fgetc(FILE *stream),char *fgets(char *s , int size , FILE *stream),与size_t fread(void *ptr,size_t size,xize_t nmemb,FILE*stream)的每次读取,都是从文件的开始位置,向后读取;在下一次循环到来之前,文件内的当前位置,已经是已读取的字符之后。
笔者一开始图方便,将读取函数fgetc()的返回值直接作为写入函数fputc()的参数,写了下面这行代码,就导致了一个比较低级的错误:
while (!feof(fp_src)) // 每次读写一个字符的第19~33行简易实现 { fputc(fgetc(fp_src), fp_dst); }
导致复制出来的文件末尾都有一个乱码:
fclose(fp_dst); // 关闭并释放文件指针堆空间 fclose(fp_src); return 0; }�
因此,此处的循环结束条件必须加上fgetc(fp_src)==EOF。
-
行与块的区别
行与块的区别,影响到设计的函数结构。
行:即数据至多包含一个换行符“\n”,遇到换行符就进行下一次循环;因此,可以以fgets()是否为NULL为循环结束条件,即文件stream到达文件末尾即可。
块:即一个固定的缓存空间,当数据块中出现换行符或字符串结束标记符等都不会受影响。因此将一个文件分为若干块,将不满一块时的判断条件作为循环结束条件,即:
*size_t fread(void ptr,size_t size,xize_t nmemb,FILE*stream)< NMEMB并将剩下的字符继续输入。
-
函数返回值
如同第二点,返回值非常重要,可以以此为切入口设计程序架构。
单字符 单行 块 读取函数 int fgetc(FILE *stream); char *fgets(char *s , int size , FILE *stream); size_t fread(void *ptr,size_t size,xize_t nmemb,FILE*stream); 成功/失败 读取到的字符/EOF(-1) 指针s/NULL nmemb/<nmemb 写入函数 int fputc(int c, FILE *stream); char *fputs(const char *s ,FILE *stream); size_t fwrite(const void *ptr,size_t size,xize_t nmemb,FILE*stream); 成功/失败 读取到的字符/EOF(-1) true/NULL nmemb/<nmemb
函数:每次读写一个字符
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
if (3 != argc) // 判断用户输入的参数是否有效。
{
perror("argument is invalid.\n");
exit(1);
}
// 分别打开待拷贝和目标拷贝文件
FILE *fp_src = fopen(argv[1], "rb"); // 以二进制形式读取第一个文件
FILE *fp_dst = fopen(argv[2], "wb"); // 以二进制形式追加写入第二个文件
if (!fp_dst || !fp_src) // 如果打开错误,则直接打印错误信息并退出.
{
perror("fopen()");
exit(1);
}
int data = fgetc(fp_src); // 设置变量,存放fgetc得到的字符
while (1)
{
if (feof(fp_src) && data == EOF) // 如果当前光标已经到文件尾,则退出循环
{
printf("Copy completed.\n");
break;
}
else if (ferror(fp_src)) // 如果出现未知错误,则退出
{
perror("fgetc()");
exit(1);
}
fputc(data, fp_dst);
data = fgetc(fp_src); // 继续存放fgetc得到的字符
}
fclose(fp_dst); // 关闭并释放打开文件申请的堆空间
fclose(fp_src);
return 0;
}
函数:每次读写一行字符
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#define BUFSIZE 100 // 设置每一个缓冲区,即每一行能复制的最大字节数
int main(int argc, const char *argv[])
{
if (3 != argc) // 判断用户输入的参数是否有效。
{
perror("argument is invalid.\n");
exit(1);
}
// 分别打开待拷贝和目标拷贝文件
FILE *fp_src = fopen(argv[1], "rb"); // 以二进制形式读取第一个文件
FILE *fp_dst = fopen(argv[2], "wb"); // 以二进制形式追加写入第二个文件
if (!fp_dst || !fp_src) // 如果打开错误,则直接打印错误信息并退出.
{
perror("fopen()");
exit(1);
}
char buf[BUFSIZE] = {0}; // 定义缓冲区变量,利用数组实现
while (1)
{
bzero(buf, BUFSIZE); // 每次操作前,进行清空缓冲区操作,以免内容泄露
if (!fgets(buf, BUFSIZE, fp_src)) // 获取源文件数据,并判断是否为NULL,如果为NULL有两种情况判断
{
if (feof(fp_src)) // 如果当前光标已经到文件尾,则表示复制完成并退出循环
{
printf("Copy completed.\n");
break;
}
else if (ferror(fp_src)) // 如果出现未知错误,则退出
{
perror("fgetc()");
exit(1);
}
}
fputs(buf, fp_dst); // 复制完成后,进行粘贴操作
}
fclose(fp_dst); // 关闭并释放打开文件申请的堆空间
fclose(fp_src);
return 0;
}
函数:每次读写一个块字符
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#define SIZE 100 // 设置每一块能容纳的最大字节数
#define NMEMB 5 // 设置块数
int main(int argc, const char *argv[])
{
if (3 != argc) // 判断用户输入的参数是否有效。
{
perror("argument is invalid.\n");
exit(1);
}
// 分别打开待拷贝和目标拷贝文件
FILE *fp_src = fopen(argv[1], "rb"); // 以二进制形式读取第一个文件
FILE *fp_dst = fopen(argv[2], "wb"); // 以二进制形式追加写入第二个文件
if (!fp_dst || !fp_src) // 如果打开错误,则直接打印错误信息并退出.
{
perror("fopen()");
exit(1);
}
char buf[SIZE * NMEMB] = {0}; // 定义缓冲区变量,利用数组实现
long pre, cur;
while (1)
{
bzero(buf, SIZE * NMEMB); // 每次操作前,进行清空缓冲区操作,以免内容泄露
pre = ftell(fp_src); // 记录当前的偏移量;
if (fread(buf, SIZE, NMEMB, fp_src) < NMEMB) // 获取源文件数据,并判断异常情况,并进行异常情况判断
{
if (feof(fp_src)) // 如果当前光标已经到文件尾,则需要把剩余的内容进行复制,这里利用pre,cur实现
{
fread(buf, cur - pre, 1, fp_src);
fwrite(buf, cur - pre, 1, fp_dst);
printf("Copy completed.\n");
break;
}
else if (ferror(fp_src)) // 如果出现未知错误,则退出
{
perror("fgetc()");
exit(1);
}
}
fwrite(buf, SIZE, NMEMB, fp_dst); // 复制完成后,进行粘贴操作
}
fclose(fp_dst); // 关闭并释放打开文件申请的堆空间
fclose(fp_src);
return 0;
}