C语言:随机读写文件、实现文件复制功能

1、随机读写文件

前面介绍的文件读写函数都是顺序读写,即读写文件只能从头开始,依次读写各个数据。但在实际开发中经常需要读写文件的中间部分,要解决这个问题,就得先移动文件内部的位置指针,再进行读写。这种读写方式称为随机读写,也就是说从文件的任意位置开始读写。

实现随机读写的关键是要按要求移动位置指针,这称为文件的定位。

文件定位函数 rewind 和 fseek

移动文件内部位置指针的函数主要有两个,即 rewind() 和 fseek()。

rewind() 用来将位置指针移动到文件开头,前面已经多次使用过,它的原型为:

void rewind ( FILE *fp );

fseek() 用来将位置指针移动到任意位置,它的原型为:

int fseek ( FILE *fp, long offset, int origin );

参数说明:

fp 为文件指针,也就是被移动的文件。

offset 为偏移量,也就是要移动的字节数。之所以为 long 类型,是希望移动的范围更大,能处理的文件更大。offset 为正时,向后移动;offset 为负时,向前移动。

origin 为起始位置,也就是从何处开始计算偏移量。C 语言规定的起始位置有三种,分别为文件开头、当前位置和文件末尾,每个位置都用对应的常量来表示:

例如,把位置指针移动到离文件开头 100 个字节处:

fseek(fp, 100, 0);

值得说明的是,fseek() 一般用于二进制文件,在文本文件中由于要进行转换,计算的位置有时会出错。

文件的随机读写

在移动位置指针之后,就可以用前面介绍的任何一种读写函数进行读写了。由于是二进制文件,因此常用 fread() 和fwrite() 读写。

【示例】从键盘输入三组学生信息,保存到文件中,然后读取第二个学生的信息。

1. #include<stdio.h>
2.
3. #define N 3
4.
5. struct stu{
6. char name[10]; //姓名
7. int num; //学号
8. int age; //年龄
9. float score; //成绩
10. }boys[N], boy, *pboys;
11.
12. int main(){
13. FILE *fp;
14. int i;
15. pboys = boys;
16. if( (fp=fopen("d:\\\\demo.txt", "wb+")) == NULL ){
17. printf("Cannot open file, press any key to exit!\\n");
18. getch();
19. exit(1);
20. }
21.
22. printf("Input data:\\n");
23. for(i=0; i<N; i++,pboys++){
24. scanf("%s %d %d %f", pboys->name, &pboys->num, &pboys->age, &pboys->score);
25. }
26. fwrite(boys, sizeof(struct stu), N, fp); //写入三条学生信息
27. fseek(fp, sizeof(struct stu), SEEK_SET); //移动位置指针
28. fread(&boy, sizeof(struct stu), 1, fp); //读取一条学生信息
29. printf("%s %d %d %f\\n", boy.name, boy.num, boy.age, boy.score);
30.
31. fclose(fp);
32. return 0;
33. }

运行结果: Input data: Tom 2 15 90.5↙

Hua 1 14 99↙

Zhao 10 16 95.5↙

Hua 1 14 99.000000

2、实现文件复制功能

文件的复制是常用的功能,要求写一段代码,让用户输入要复制的文件以及新建的文件,然后对文件进行复制。能够复制的文件包括文本文件和二进制文件,你可以复制 1G 的电影,也可以复制 1Byte 的 txt 文档。

实现文件复制的主要思路是:开辟一个缓冲区,不断从原文件中读取内容到缓冲区,每读取完一次就将缓冲区中的内容写入到新建的文件,直到把原文件的内容读取完

这里有两个关键的问题需要解决:

开辟多大的缓冲区合适?缓冲区过小会造成读写次数的增加,过大也不能明显提高效率。目前大部分磁盘的扇区都是 4K 对齐的,如果读写的数据不是 4K 的整数倍,就会跨扇区读取,降低效率,所以我们开辟 4K 的缓冲区。

缓冲区中的数据是没有结束标志的,如果缓冲区填充不满,如何确定写入的字节数?最好的办法就是每次读取都能返回读取到的字节数。

fread() 的原型为:

size_t fread ( void *ptr, size_t size, size_t count, FILE *fp );

它返回成功读写的块数,该值小于等于 count。如果我们让参数 size 等于 1,那么返回的就是读取的字节数。

注意:fopen()一定要以二进制的形式打开文件,不能以文本形式打开,否则系统会对文件进行一些处理,如果是文本文件,像.txt 等,可能没有问题,但如果是其他格式的文件,像.mp4, .rmvb, .jpg 等,复制后就会出错,无法读取。

代码实现:

1. #include <stdio.h>
2. #include <stdlib.h>
3.
4. int copyFile(char *fileRead, char *fileWrite);
5.
6. int main(){
7. char fileRead[100]; // 要复制的文件名
8. char fileWrite[100]; // 复制后的文件名
9. 
10. // 获取用户输入
11. printf("要复制的文件:");
12. scanf("%s", fileRead);
13. printf("将文件复制到:");
14. scanf("%s", fileWrite);
15.
16. // 进行复制操作
17. if( copyFile(fileRead, fileWrite) ){
18. printf("恭喜你,文件复制成功!\\n");
19. }else{
20. printf("文件复制失败!\\n");
21. }
22.
23. return 0;
24. }
25.
26. /**
27. * 文件复制函数
28. * @param fileRead 要复制的文件
29. * @param fileWrite 复制后文件的保存路径
30. * @return int 1: 复制成功;2: 复制失败
31. **/
32. int copyFile(char *fileRead, char *fileWrite){
33. FILE *fpRead; // 指向要复制的文件
34. FILE *fpWrite; // 指向复制后的文件
35. int bufferLen = 1024*4; // 缓冲区长度
36. char *buffer = (char*)malloc(bufferLen); // 开辟缓存
37. int readCount; // 实际读取的字节数
38.
39. if( (fpRead=fopen(fileRead, "rb")) == NULL || (fpWrite=fopen(fileWrite, "wb")) == NULL ){
40. printf("Cannot open file, press any key to exit!\\n");
41. getch();
42. exit(1);
43. }
44.
45. // 不断从 fileRead 读取内容,放在缓冲区,再将缓冲区的内容写入 fileWrite
46. while( (readCount=fread(buffer, 1, bufferLen, fpRead)) > 0 ){
47. fwrite(buffer, readCount, 1, fpWrite);
48. }
49.
50. free(buffer);
51. fclose(fpRead);
52. fclose(fpWrite);
53.
54. return 1;
55. }

运行结果:

要复制的文件:d://1.mp4
将文件复制到:d://2.mp4
恭喜你,文件复制成功!

如果文件不存在,会给出提示,并终止程序:

要复制的文件:d://123.mp4
将文件复制到:d://333.mp4
d://cyuyan.txt: No such file or directory

第 46 行是文件复制的核心代码。通过 fread()函数,每次从 fileRead 文件中读取 bufferLen 个字节,放到缓冲区,再通过 fwrite()函数将缓冲区的内容写入 fileWrite 文件。

正常情况下,每次会读取 bufferLen 个字节,即 readCount=bufferLen;如果文件大小不足 bufferLen 个字节,或者读取到文件末尾,实际读取到的字节就会小于 bufferLen,即 readCount<bufferLen。所以通过 fwrite()写入文件时,应该以 readCount 为准。

相关推荐
java1234_小锋几秒前
MyBatis的核心组件有哪些?
java·开发语言
2401_858286113 分钟前
118.【C语言】数据结构之排序(堆排序和冒泡排序)
c语言·数据结构·算法
不听话的好孩子9 分钟前
基于深度学习(HyperLPR3框架)的中文车牌识别系统-python程序开发测试
开发语言·python·深度学习
阿松のblog11 分钟前
pyQt5实现目标检测可视化001
开发语言·qt·目标检测
zyx没烦恼14 分钟前
【C++11】包装器
开发语言·c++
No regret.25 分钟前
JVM内存模型、垃圾回收机制及简单调优方式
java·开发语言·jvm
bear_7928 分钟前
Go操作MySQL
开发语言·go
明月看潮生30 分钟前
青少年编程与数学 02-004 Go语言Web编程 21课题、应用部署
开发语言·青少年编程·应用部署·编程与数学·goweb
云上的阿七1 小时前
《云计算能不能真正实现按需付费?》
开发语言·云计算·perl
Yhame.6 小时前
深入理解 Java 中的 ArrayList 和 List:泛型与动态数组
java·开发语言