C语言:使用fread和fwrite实现文件拷贝
前置基础
函数一: fread
函数声明:
size_t fread(void *buf, sizeof(void), len, FILE *p);
- buf , 从p文件中读取的数据
- sizeof(void) ,每次从p中读取数据的大小
- len ,每次从p中读取数据的个数
- p , 打开的文件地址
解释:
sizeof( void ) 和 len 是搭配使用的,它们的作用就是令临时空间使用完:
char a = 0;
fread(&a, 1, 1, p);
//一次使用完
char b[10] = { 0 };
fread(b, sizeof(char), sizeof(b), p);
//每次读取一个char 读取10次。
char c[10] = { 0 };
fread(c, 1, 1, p);
返回值:
fread 的返回值是每次从p中读取的数据的个数,也就是len 的值。
函数二:fwrite
函数声明:
size_t fwrite(void *buf, sizeof(void), int nums, FILE *p);
- buf , 临时空间,每次从buf中读取数据,然后写入p中
- sizeof(void) ,每次往p中写入数据块的大小
- nums ,每次往p中写入的数据
- p , 打开的文件地址
解释:
fwrite的使用于fread是相互映照的。
返回值:
返回每次写入的次数。
基本思路:
- 打开要读取的文件a
- 打开要写入的文件b
- 读取a ,存放到临时的空间中
- 读取临时空间的数据,写入b
- 关闭a
- 关闭b
- 如果使用的是堆空间,释放临时空间
代码实现:
mycopy.c :
#include <stdio.h>
void mycopy(char *str1, char *str2)
{
FILE *p = fopen(str1, "rb");
FILE *p1 = fopen(str2, "wb");
unsigned int index = 0;
while(1)
{
char buf[1024] = { 0 };
fread(buf, sizeof(char), 1024, p);
if(feof(p))
break;
fwrite(buf, sizeof(char), 1024, p1);
index++;
}
fclose(p);
fclose(p1);
printf("%d\n", index);
}
int main(int arge, char **arges)
{
if(arge < 3)
return 0;
mycopy(arges[1], arges[2]);
return 0;
}
解析:
上述代码主要通过一个1kB的空间来实现缓存区,通过缓存区把一个文件的数据读出来,然后把数据写入要复制的文件内。
#编译
gcc -o a mycopy.c
#运行
#把a.txt 复制一份为b.txt
a test1.mp4 test2.mp4
运行结果:
通过运行得到以下下文件
[使用fread和fwrite实现文件拷贝]$ ll
total 21573
-rwxr-xr-x 1 Administrator 197121 55425 三月 21 16:00 a.exe*
-rw-r--r-- 1 Administrator 197121 555 三月 21 18:53 mycopy.c
-rw-r--r-- 1 Administrator 197121 11014383 一月 14 2017 test1.mp4
-rw-r--r-- 1 Administrator 197121 11014144 三月 21 18:59 test2.mp4
代码失误:
上述代码虽然可以把文件给复制下来,但还不能完美的复制下来,test2.mp4 尾数是144,test1.mp4 尾数是383,明显比原文件小。
错误原因:
-
每次都要往文件中写入1024个,最后一次会读取到文件末尾,跳出循环,不够1024会舍弃数据。 测试代码: 创建一个a.txt abcde
文件有5个字节的空间 -rw-r--r-- 1 Administrator 197121 5 三月 21 19:15 a.txt
代码 void mycopy_test(char *str1, char *str2)
{
FILE *p = fopen(str1, "rb");
FILE *p1 = fopen(str2, "wb");
unsigned int index = 0;
while(1)
{
char buf[10] = { 0 };
fread(buf, sizeof(char), 3, p);
if(feof(p))
break;
fwrite(buf, sizeof(char), 3, p1);
index++;
}
fclose(p);
fclose(p1);
printf("%d\n", index);
}
当最后读取空间不够时,会跳出循环。 改进后代码: void mycopy_test(char *str1, char *str2)
{
FILE *p = fopen(str1, "rb");
FILE *p1 = fopen(str2, "wb");
unsigned int index = 0;
while(!feof(p))
{
char buf[10] = { 0 };
fread(buf, sizeof(char), 3, p);
fwrite(buf, sizeof(char), 3, p1);
index++;
}
fclose(p);
fclose(p1);
printf("%d\n", index);
}
改进后的代码,最后读出不够3个字节,“abcde” ,只读出了2个字节,然后写入到文件b.txt中。 测试结果: [使用fread和fwrite实现文件拷贝]$ ll
total 21582
-rwxr-xr-x 1 Administrator 197121 55967 三月 21 19:28 a.exe*
-rw-r--r-- 1 Administrator 197121 5 三月 21 19:15 a.txt
-rw-r--r-- 1 Administrator 197121 6 三月 21 19:28 b.txt
-rw-r--r-- 1 Administrator 197121 910 三月 21 19:28 mycopy.c
可以看到b.txt文件比原文件又大了一个字节。 -
所以我们找到了第二个错误,写入字节没控制好。 解决方法:通过设置读文件字节的返回值,来设置写文件的次数。 代码实现: void mycopy_test(char *str1, char *str2)
{
FILE *p = fopen(str1, "rb");
FILE *p1 = fopen(str2, "wb");
unsigned int index = 0;
while(!feof(p))
{
char buf[10] = { 0 };
int res = fread(buf, sizeof(char), 3, p);
fwrite(buf, sizeof(char), res, p1);
index++;
}
fclose(p);
fclose(p1);
printf("%d\n", index);
}
res 是 fread 读出1个字节的次数,然后把它设为写入1个字节的次数,这样就不会复制失败任何一个字节。
代码提升:
当我们发现我们可以通过设置临时空间来实现文件的复制的时候,我们会非常高兴,但当一个文件非常小的时候,我们设置了一个非常大的临时空间,这就浪费了内存;当文件非常大的时候,我们设计了一个非常小的临时空间,我们函数的循环的时间久会变长,也是非常难受,那有没有一种非常好的方法,每次用多少内存就开辟多少的临时空间呢?
当然是有的!
我们可以通过使用stat函数来获取文件的字节大小,根据文件的大小开辟对应的内存空间,并合理的规划文件的拷贝次数,从而达到文件被拷贝的目的。
代码实现:
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#define NUM 1024 * 10
void mycopy_dynamic(char *str1, char *str2)
{
FILE *p = fopen(str1, "rb");
FILE *p1 = fopen(str2, "wb");
struct stat st = { 0 };
stat(str1, &st);
int size = st.st_size;
if(size >= NUM)
size = NUM;
char *buffer = malloc(size);
unsigned int index = 0;
while(!feof(p))
{
int res = fread(buffer, sizeof(char), size, p);
fwrite(buffer, sizeof(char), res, p1);
index++;
}
free(buffer);
fclose(p);
fclose(p1);
printf("%d\n", index);
}
int main(int arge, char **arges)
{
if(arge < 3)
return 0;
mycopy_dynamic(arges[1], arges[2]);
return 0;
}
通过上述代码,我们实现了每次拷贝不超过10KB的文件一次拷贝完的操作,如果文件大于10KB,就只申请10KB的内存空间,实现了动态开辟内存来存储临时数据。
测试结果
[使用fread和fwrite实现文件拷贝]$ a test1.mp4 test2.mp4
1076
[使用fread和fwrite实现文件拷贝]$ ll
total 21586
-rwxr-xr-x 1 Administrator 197121 57451 三月 22 15:20 a.exe*
-rw-r--r-- 1 Administrator 197121 5 三月 21 19:15 a.txt
-rw-r--r-- 1 Administrator 197121 6 三月 21 19:28 b.txt
-rw-r--r-- 1 Administrator 197121 1593 三月 22 15:22 mycopy.c
-rw-r--r-- 1 Administrator 197121 11014383 一月 14 2017 test1.mp4
-rw-r--r-- 1 Administrator 197121 11014383 三月 22 15:24 test2.mp4
完结!
|