在程序运行时所有对数据的运算结果都是在内存中,程序一旦退出后所有的结果都将丢失。为了存储程序运行的结果,都会将数据持久化到磁盘上,当程序再次启动就可以读取磁盘上存储的数据继续处理。
磁盘上的数据都是以文件的形式存在的。在C语言中,提供了标准的文件操作函数实现文件的读取和写入。
在C语言中操作文件一般分为三步:
- 打开文件
- 读取或写入文件
- 关闭文件
下面总结一下这三步中涉及到的函数以及相关的关键字。
一、打开文件
打开文件通过函数 fopen() 。函数原型是:
FILE * fopen(const char * _Filename, const char * _Mode);
函数两个参数:第一个参数是文件路径,第二个参数是文件打开模式。函数返回值是一个文件指针。打开模式有下面几种
打开模式 | 模式描述 |
---|---|
r | 只读模式:文件不存在则打开失败;成功打开文件后文件指针位于文件开头;可以从文件任意位置读取内容 |
w | 只写模式:文件不存在创建文件,文件已经存在清空文件内容;成功打开文件后文件指针位于文件开头;可以向文件中任意位置写入内容,写入时会覆盖原有位置的内容 |
a | 追加写模式:文件不存在创建文件,文件已存在不清空文件内容;成功打开文件后文件指针位于文件结尾;向文件写入内容时追加在文件尾部 |
rb | 操作二进制文件,模式特性与 r 相同 |
wb | 操作二进制文件,模式特性与 w 相同 |
ab | 操作二进制文件,模式特性与 a 相同 |
上面这些只能以只读或只写模式打开文件,下面这些可以同时支持读和写的方式打开文件:
打开模式 | 模式描述 |
---|---|
r+ | 以读写模式(既可读取也可以写入)打开文件,文件不存在打开失败;成功打开文件后文件指针位于文件开头;打开文件后不会清空文件原有内容;读取或写入都可以在文件的任意位置,在写入内容时,会覆盖原有位置的内容 |
w+ | 以读写模式(既可读取也可以写入)打开文件,文件不存在则新建文件;成功打开文件后文件指针位于文件开头;打开文件后会清空文件原有内容;读取或写入都可以在文件的任意位置,在写入内容时,会覆盖原有位置的内容 |
a+ | 以读写模式(既可读取也可以写入)打开文件,文件不存在则新建文件;成功打开文件后文件指针位于文件结尾;打开文件后不会清空文件原有内容;读取内容时可以在文件的任意位置,在写入内容时会追加在文件尾部 |
r+b / rb+ | 操作二进制文件,模式特性与 r+ 相同 |
w+b / wb+ | 操作二进制文件,模式特性与 w+ 相同 |
a+b / ab+ | 操作二进制文件,模式特性与 a+ 相同 |
比如以只读模式打开文件:
FILE * file = fopen("E:\\demo.txt", "r");
二、读文件或写文件
读取文件有四个函数:fgetc()、fgets()、fread()、fscanf();写文件也对应有四个函数:fputc()、fputs()、fwrite()、fprintf()。观察函数原型就知道,每一种读取文件的方式都对应一种类似的写入方式。下面分别说一下各个函数使用方式,所有的示例程序都是操作文本文件的,如果将模式改为带b
,就完全适用于操作二进制文件:
1、fgetc : 一次读取一个字符
函数原型如下:
int fgetc(FILE *_File);
函数参数是打开的文件指针,函数返回值是读取到的字符:
#include <stdio.h>
int main() {
// 打开文件
FILE * file = fopen("E:\\demo.txt", "r");
// 读取文件内容
int c;
while((c = fgetc(file)) >= 0) {
printf("%c", c);
}
printf("\n");
// 关闭文件
fclose(file);
return 0;
}
2、fgets : 一次读取多个字符,将读取结果放入字符数组中
它从文件指针指向的位置开始读取,直到遇到换行符、文件结束或已读取指定-1个字符。它将读取的字符串包括换行符保存在字符数组中,并返回字符数组的指针。函数的返回值用于判断函数是否正常执行:如果返回 NULL,则表示发生错误或者读取到文件末尾;如果返回非 NULL,则表示读取成功。
函数原型如下:
char * fgets(char * _Buf, int _MaxCount, FILE * _File);
函数三个参数:第一个参数是字符数组,第二个参数是每次读取最大字符数量,第三个参数是文件指针。
#include <stdio.h>
int main() {
// 打开文件
FILE * file = fopen("E:\\demo.txt", "r");
// 读取文件内容
char str[4] = { 0 };
int len = sizeof(str);
while (fgets(str, len, file) != NULL) {
printf("%s", str);
}
printf("\n");
// 关闭文件
fclose(file);
return 0;
}
3、fread : 一次读取文件指定长度内容到数组中
它可以读取文本文件也可以读取二进制文件。函数原型如下:
size_t fread(void * _DstBuf, size_t _ElementSize, size_t _Count, FILE * _File);
函数四个参数:第一个参数是接收数据的数组,第二个参数是数组中每个元素占用的字节,第三个参数是一次读取数据的最大数量,一般为接收数据数组的长度,第四个参数是文件指针。函数返回值是这次读取的字节数
#include <stdio.h>
int main() {
// 打开文件
FILE * file = fopen("E:\\demo.txt", "r");
// 读取文件内容
char str[4] = { 0 };
size_t eleSize = sizeof(char);
size_t len = sizeof(str);
size_t cnt;
while ((cnt = fread(str, eleSize, len, file)) != 0) {
for (int i = 0; i < cnt; ++i) {
printf("%c", str[i]);
}
}
printf("\n");
// 关闭文件
fclose(file);
return 0;
}
4、fscanf:按照指定格式读取文件内容
该函数与scanf函数类似,只是它是从文件接收数据,函数原型如下:
int fscanf(FILE *__stream, const char *__format, ...);
与scanf函数原型:
int scanf(const char *__format, ...);
比较,多了一个文件指针参数,其他参数一致:
#include <stdio.h>
int main() {
// 打开文件
FILE * file = fopen("E:\\demo.txt", "r");
// 读取文件内容
char str[64];
fscanf(file, "%s", str);
printf("%s\n", str);
// 关闭文件
fclose(file);
return 0;
}
5、fputc:一次写一个字符
函数原型:
int fputc(int _Ch, FILE *_File);
函数第一个参数是写入的字符,第二个参数是函数指针;函数返回值是写入的字符。
#include <stdio.h>
int main() {
// 打开文件
FILE * file = fopen("E:\\demo.txt", "w+");
// 写内容到文件
fputc('H', file);
fputc('E', file);
fputc('L', file);
fputc('L', file);
fputc('O', file);
// 重新定位到文件开始
rewind(file);
// 读取文件内容
char str[8];
fgets(str, sizeof(str), file);
printf("%s\n", str);
// 关闭文件
fclose(file);
return 0;
}
6、fputs:一次写一个字符串到文件
函数原型:
int fputs(const char * _Str, FILE * _File);
函数的第一个参数是要写入的字符串,第二个参数是文件指针。函数返回值表示写入结果,如果写入成功返回一个非负数;如果发生写入错误,则返回 EOF,并设置 errno 以指示错误。EOF常被定义为-1。
#include <stdio.h>
int main() {
// 打开文件
FILE * file = fopen("E:\\demo.txt", "w+");
// 写内容到文件
int rs = fputs("hello,world!", file);
printf("%d\n", rs);
// 重新定位到文件开始
rewind(file);
// 读取文件内容
char str[16];
fgets(str, sizeof(str), file);
printf("%s\n", str);
// 关闭文件
fclose(file);
return 0;
}
7、fwrite:一次写一批数据到文件
函数原型:
size_t fwrite(const void * _Str, size_t _Size, size_t _Count, FILE * _File);
第一个参数写入数据的指针,第二个参数是每个元素占用的字节,第三个参数是要写入元素个数,第四个参数是文件指针。函数的返回值是写入后的指针位置:
#include <stdio.h>
int main() {
// 打开文件
FILE * file = fopen("E:\\demo.txt", "w+");
// 写内容到文件
char data[] = "hello,world!";
size_t rs = fwrite(data, sizeof(char), sizeof(data), file);
printf("%zu\n", rs);
// 重新定位到文件开始
rewind(file);
// 读取文件内容
char str[16];
fgets(str, sizeof(str), file);
printf("%s\n", str);
// 关闭文件
fclose(file);
return 0;
}
8、fprintf:输出指定格式数据到文件中
函数原型:
int fprintf (FILE * __stream, const char *__format, ...);
与printf用法相似,函数输出结果到文件中,函数返回值是写入的字节数:
#include <stdio.h>
int main() {
// 打开文件
FILE * file = fopen("E:\\demo.txt", "w+");
// 写内容到文件
int rs = fprintf(file, "%s", "hello,world!");
printf("%d\n", rs);
// 重新定位到文件开始
rewind(file);
// 读取文件内容
char str[16];
fgets(str, sizeof(str), file);
printf("%s\n", str);
// 关闭文件
fclose(file);
return 0;
}
三、关闭文件
文件操作完成后要及时关闭,减少不必要的问题发生。关闭文件函数原型是:
int fclose(FILE * _File);
函数只需要一个参数文件指针,当 fclose 函数成功关闭文件时,它返回 0。如果发生错误,它将返回 EOF 并设置 errno。
四、删除文件
删除文件有两个函数remove和unlink,它们都可以实现文件删除功能。如果删除成功,remove函数返回0;如果删除失败,返回非零值。
#include <stdio.h>
int main() {
// 删除文件
int rs1 = remove("E:\\redis-7.2.5.tar.gz");
printf("%d\n", rs1);
int rs2 = unlink("E:\\redis-7.2.5-1.tar.gz");
printf("%d\n", rs2);
return 0;
}
五、获取文件大小和时间
获取文件大小是通过移动指向文件位置的指针到文件结尾,再获取指向位置来实现的:
#include <stdio.h>
int main() {
FILE * file = fopen("E:\\redis-7.2.5.tar.gz", "rb");
// 获取文件大小
fseek(file, 0, SEEK_END); // 定位到文件末尾
long size = ftell(file); // 获取文件大小
printf("file size : %ld\n", size);
// 重新定位到文件开头
rewind(file);
// 关闭文件
fclose(file);
return 0;
}
c语言中获取文件的时间是通过 stat 结构体和 stat() 函数实现的:
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
int main() {
char file_name[] = "E:\\redis-7.2.5.tar.gz";
struct stat file_stat;
if (stat(file_name, &file_stat) == 0) {
printf("file create time : %s", ctime(&file_stat.st_ctime));
printf("file modify time : %s", ctime(&file_stat.st_mtime));
printf("file access time : %s", ctime(&file_stat.st_atime));
}
return 0;
}
以上就是C语言有关文件操作的常用函数。下面示例如何使用上面的函数完成文件复制:
#include <stdio.h>
// C语言中没有定义字节类型,而char刚好占用一个字节,可以使用 unsigned char 表示字节类型
typedef unsigned char byte;
int copy_file(const char * src_path, const char * dest_path) {
// 打开文件
FILE * src_file = fopen(src_path, "rb");
if (src_file == NULL) {
printf("source file open fail!");
return -1;
}
FILE * dest_file = fopen(dest_path, "wb");
if (dest_file == NULL) {
printf("target file open fail!");
fclose(src_file);
return -1;
}
// 写内容到文件
byte bytes[1024];
size_t ele_size = sizeof(byte);
size_t buff_size = sizeof(bytes);
size_t len = 0; // 读取到文件内容字节数
while ((len = fread(bytes, ele_size, buff_size, src_file)) > 0) {
fwrite(bytes, ele_size, len, dest_file);
}
// 关闭文件
fclose(src_file);
fclose(dest_file);
// 成功返回1
return 1;
}
int main() {
copy_file("E:\\redis-7.2.5.tar.gz", "E:\\cp-redis-7.2.5.tar.gz");
return 0;
}