深入剖析C语言文件操作
最编程
2024-08-13 10:24:40
...
在 C 语言中,文件操作的函数大多包含在 头文件中,使用时记得 #include。
一、打开和关闭文件
1. 打开文件
FILE * fopen ( const char * filename, const char * mode );
打开一个文件,成功则返回文件的指针,失败则返回 NULL。
任何打开的文件在不需要使用后都必须关闭。
模式:
- r 打开只读文件,文件必须存在
- r+ 打开可读写文件,文件必须存在
- w 打开只写文件,文件不存在会创建,文件存在会覆盖原内容
- w+ 打开可读写文件,文件不存在会创建,文件存在会覆盖原内容
- a 附加形式打开只写文件
- a+ 附加形式打开可读写文件
- b 加在上面模式之后,表示二进制方式打开,例如 “rb”、”r+b”、”wb” 等等
使用文本文件方式读写文件,在 Windows 下换行 \n 会自动替换为 \r\n,使用二进制方式则不会。
2. 关闭文件
int fclose ( FILE * stream );
关闭一个文件指针,成功返回0,失败返回 EOF,其值通常为 -1。
3. 代码示例
#include <stdio.h>
int main ()
{
FILE * pFile;
pFile = fopen ("D:\\myfile.txt", "wb"); // 以二进制方式打开只写文件
if (pFile != NULL)
{
fputs ("fopen example", pFile);
fclose (pFile); // 用完后一定要记得关闭
}
return 0;
}
二、读文件
1. 读取一个字符
int fgetc ( FILE * stream );
返回读取到的字符,读到文件末尾则返回 EOF。
示例:
#include <stdio.h>
int main ()
{
FILE * pFile;
int c;
int n = 0;
pFile = fopen ("D:\\myfile.txt", "r");
if (pFile == NULL) perror ("Error opening file"); // 打开失败
else
{
while (c != EOF)
{
c = fgetc (pFile); // 获取一个字符
if (c == '$') n++; // 统计美元符号 '$' 在文件中出现的次数
}
fclose (pFile); // 一定记得要关闭文件
printf ("The file contains %d dollar sign characters ($).\n",n);
}
return 0;
}
2. 读取字符串
char * fgets ( char * str, int num, FILE * stream );
- str 是将读取到的内容复制到的目标字符串数组
- num 是一次读取的大小
这里还要介绍一下 feof(FILE * stream) 函数,它用于判断是否已经到文件末尾了,如果到了文件末尾则会返回 0。
示例:
#include <stdio.h>
#include <string.h>
int main()
{
FILE * pFile;
char mystring[100] = { 0 };
pFile = fopen("D:\\myfile.txt", "r");
if (pFile == NULL) perror ("Error opening file");
else
{
while (!feof(p)) // 如果还没有到文件末尾
{
memset(s, 0, sizeof(s)); // 重置是个好习惯
fgets(s, sizeof(s), p); // 一次读取 100 个字符
printf("%s", s);
}
fclose(p); // 一定记得要关闭文件
}
return 0;
}
3. 读取指定内存大小
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
- ptr 为目标内存块,其大小至少为 size * count
- size 是一次读取的字节大小
- count 是一次读取多少个 size
- 返回值是成功读取的字节大小
示例:
#include <stdio.h>
#include <string.h>
int main()
{
FILE *pFile = fopen("I:\\test.txt", "rb");
if (pFile == NULL)
{
perror ("Error opening file");
return -1;
}
char buf[100] = { 0 };
while (!feof(pFile)) // 如果还没有到文件末尾
{
memset(buf, 0, sizeof(buf));
size_t len = fread(buf, sizeof(char), sizeof(buf), pFile);
printf("buf: %s, len: %d\n", buf, len);
}
fclose(pFile);
return 0;
}
三、写文件
和读文件相对应,也分以下三个方法:
1. 写一个字符
int fputc ( int character, FILE * stream );
成功则返回写入的字符,失败返回 EOF。
2. 写入字符串
int fputs ( const char * str, FILE * stream );
成功返回一个正数,失败返回 EOF。
3. 写入指定内存大小
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
返回成功写入的内存大小,单位为字节。
4. 示例:文件拷贝
一个文件拷贝的例子:
#include <stdio.h>
#include <string.h>
int main()
{
FILE *src = fopen("D:\\src.avi", "rb"); // 源文件
if (src == NULL)
{
perror ("Error opening file");
return -1;
}
FILE *dst = fopen("D:\\dst.avi", "wb"); // 需要拷贝的目标文件
char buf[1024 * 64] = { 0 }; // 一次读取 64k 的大小
while (!feof(p))
{
memset(buf, 0, sizeof(buf));
size_t len = fread(buf, sizeof(char), sizeof(buf), src);
// 注意这里写入的大小一定要使用读取返回的大小 len,
// 否则很可能出现文件末尾多写入了很多个 0 的情况
fwrite(buf, sizeof(char), len, dst);
}
fclose(src);
fclose(dst);
printf("copy end\n");
return 0;
}
四、其它常用函数
1. 文件指针位置重定位
之前我们的文件操作都只能一步步往下读写,想回头的话怎么办,就需要使用下面这个函数了
int fseek ( FILE * stream, long int offset, int origin );
- 成功返回 0,失败返回一个非 0 数
- offset 指相应对 origin 位置处的偏移量,单位为字节
- origin 是指针的参考位置,有以下三个值可以选择
#define SEEK_CUR 1 // 当前位置
#define SEEK_END 2 // 末尾
#define SEEK_SET 0 // 开头
2. 获取指针当前位置
long int ftell ( FILE * stream );
成功返回指针相对于开头的偏移量,失败返回 -1。
3. 示例:获取文件大小
使用以上两个函数,可以轻松获取到文件的大小:
#include <stdio.h>
int main ()
{
FILE * pFile;
long size = 0; // 文件大小
pFile = fopen ("D:\\myfile.txt","rb");
if (pFile==NULL) perror ("Error opening file");
else
{
fseek (pFile, 0, SEEK_END); // 直接定位到文件末尾
size = ftell (pFile); // 获取到当前指针的偏移量,即文件大小
fclose (pFile);
printf ("Size of myfile.txt: %ld bytes.\n", size);
}
return 0;
}
4. 将缓冲区内容写入文件
实际上,当我们在执行文件写入操作时,写入的内容只是保留在了一个缓冲区内,并没有实时写入到文件中去,而是等到 fclose 或者程序终止时才写入。
使用下面这个函数则可以将缓冲区的内容即时写入到文件中去:
int fflush ( FILE * stream );
- 成功返回 0,失败返回 EOF。
- 优点:
防止由于断电或者死机造成重要数据没有写入到文件中而导致数据丢失。 - 缺点:
磁盘读写次数增加,影响程序执行效率,降低磁盘使用寿命。
5. fprintf() 和 fscanf()
定义如下:
int fprintf ( FILE * stream, const char * format, ... );
int fscanf ( FILE * stream, const char * format, ... );
和 printf() 与 scanf() 使用方式基本相同,只不过他们是把结果读入或者写入到文件中去了。
示例:
#include <stdio.h>
int main()
{
char str[80];
float f;
FILE * pFile;
pFile = fopen("D:\\myfile.txt", "w+");
// 在文件中写入 "PI is 3.1416" 这个内容
fprintf(pFile, "%s is %f", "PI", 3.14159f);
rewind(pFile); // rewind 函数将指针重新回到起始位置
fscanf(pFile, "%s is %f", str, &f);
fclose(pFile);
printf("I have read: %f and %s \n", f, str);
return 0;
}
五、删除和重命名文件
1. 重命名文件
int rename ( const char * oldname, const char * newname );
删除成功返回 0,失败返回非 0。
2. 删除文件
int remove ( const char * filename );
删除成功返回 0,失败返回非 0。
这两个方法都很简单,就不多说了。
六、读、写一个文件示例
int readFile(const char *filePath, void **data)
{
FILE *fp = fopen(filePath, "rb");
if (fp == nullptr) {
return -1;
}
fseek(fp, 0, SEEK_END);
auto len = ftell(fp);
rewind(fp);
*data = (char *) malloc(sizeof(char) * len);
fread(*data, len, 1, fp);
fclose(fp);
return len;
}
int writeFile(const char *filePath, void *data, unsigned int len)
{
FILE *file = fopen(filePath, "wb");
if (file == NULL) {
return -1;
}
int size = fwrite(data, 1, len, file);
fclose(file);
return 0;
}
下一篇: 总结C语言文件的读写操作