文件操作

打开文件

1
FILE *freopen(const char *pathname, const char *mode, FILE *stream);

pathname:可以填写相对路径或绝对路径

stream:文件流

mode:模式(重点)

mode 含义 文件不存在 文件存在
r 只读 防护NULL(错误) 从文件头开始读取
w 清空写入 创建新文件 清空文件内容,从头开始写入
a 追加写入 创建新文件 在文件末尾追加内容
r+ 读写 防护NULL(错误) 从文件头开始读写
w+ 读写清空 创建新文件 清空文件内容,从头开始读写
a+ 读写追加 创建新文件 在文件末尾追加内容,但允许读写

上面是以文本形式进行操作,如果你想以二进制形式操作文件,只需要在 mode 后面添加 b 即可。

二进制操作.png

关闭文件

1
int fclose(FILE *stream);

文件流不再需要使用,记得用 fclose 关闭。

读写文件

文本

(一)读写单个字符

1
2
3
int fgetc(FILE *stream);	// 从文件流 stream 中读取单个字符

int fputc(int c, FILE *stream); // 把单个字符写入文件流 stream

示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void readFile(const char* path) {
FILE* src = fopen(path, "r");
if (!src) {
fprintf(stderr, "src fopen failed!!!");
exit(-1);
}

FILE* dst = fopen("b.txt", "w");
if (!dst) {
fprintf(stderr, "dst fopen failed!!!");
exit(-1);
}

char c;
while ((c = fgetc(src)) != EOF) // 按单个字符读取和写入 fget,fput
{
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M')) {
fputc(c + 13, dst);
}
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z')) {
fputc(c - 13, dst);
}
else {
fputc(c, dst);
}
}

fclose(src);
fclose(dst);
}

(二)读写一行

1
2
3
char *fgets(char *s, int size, FILE *stream);	// 从文件流 stream 读取指定长度的字符串

int fputs(const char *s, FILE *stream); // 读取指定长度的字符串 写入文件流 stream

示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#define MAX_LEN 128
void readFile(const char* path) {
FILE* src = fopen(path, "r");
if (!src) {
fprintf(stderr, "src fopen failed!!!");
exit(-1);
}

FILE* dst = fopen("b.txt", "w");
if (!dst) {
fprintf(stderr, "dst fopen failed!!!");
exit(-1);
}

char* line[MAX_LEN];
char* message[MAX_LEN];
int index = 1;
while ((fgets(line, MAX_LEN, src) != NULL)) // 按行读取和写入 fgets,fputs
{
sprintf(message, "%d. %s", index, line);
fputs(message, dst);
index++;
}

fclose(src);
fclose(dst);
}

(三)格式化地读写

1
2
3
int fscanf(FILE *stream, const char *format, ...);

int fprintf(FILE *stream, const char *format, ...);

示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#define MAX_LEN 128
#define NAME_LEN 20

typedef struct student
{
int id;
char name[NAME_LEN];
char sex;
int chinese;
int math;
int english;
} Student;

void readFile(const char* path) {
FILE* src = fopen(path, "r");
if (!src) {
fprintf(stderr, "src fopen failed!!!");
exit(-1);
}

FILE* dst = fopen("b.txt", "w");
if (!dst) {
fprintf(stderr, "dst fopen failed!!!");
exit(-1);
}

// 按格式读取和写入 fscanf,fprintf
for (;;) {
Student s;
int n = fscanf(src, "%d%s %c%d%d%d", &s.id, s.name, &s.sex, &s.chinese, &s.math, &s.english);
if (n != 6) break;

s.chinese = s.chinese * 0.85;
s.math = s.math * 0.9;
s.english = s.english * 0.8;

fprintf(dst, "%d %s %c %d %d %d\n", s.id, s.name, s.sex, s.chinese, s.math, s.english);
}

fclose(src);
fclose(dst);
}

二进制

1
2
3
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);

fread 的返回值是读取的大小,这个返回值将作为 fwrite size 参数。不然读取超出已读的长度会出现错误。

示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#define MAX_LEN 128
void readFile(const char* path) {
FILE* src = fopen(path, "r");
if (!src) {
fprintf(stderr, "src fopen failed!!!");
exit(-1);
}

FILE* dst = fopen("b.txt", "w");
if (!dst) {
fprintf(stderr, "dst fopen failed!!!");
exit(-1);
}

char* data[MAX_LEN];
int n = 0;
while ((n = fread(data, 1, MAX_LEN, src)) > 0) {
fwrite(data, 1, n, dst); // 容易犯错的地方,误填为MAX_LEN,应该填写读的字节数 n
}

fclose(src);
fclose(dst);
}

移动文件位置

1
2
3
4
5
int fseek(FILE *stream, long offset, int whence);	// 移动文件位置

long ftell(FILE *stream); // 获取当前文件位置

void rewind(FILE *stream); // 回到文件起始位置

其中 whence 参数有三个取值:

SEEK_SET: 文件开头,表示从文件的起始位置移动文件指针。

SEEK_CUR: 当前文件指针位置,表示从当前指针位置移动。

SEEK_END: 文件末尾,表示从文件末尾开始移动。

示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
char* readFile(const char* path) {
FILE* fp = fopen(path, "r");
if (!fp) {
exit(-1);
}

fseek(fp, 0, SEEK_END);
long int len = ftell(fp);
rewind(fp);

if (len == -1) {
exit(-1);
}

char* data = malloc(sizeof(char) * (len + 1));
if (!data) {
exit(-1);
}

size_t reNum = fread(data, 1, len, fp);
if (reNum != len) {
free(data);
fclose(fp);
exit(-1);
}
data[reNum] = '\0'; // 根据实际读取的长度,并在其后添加 空字符

fclose(fp);
return data;
}

由于我们要把某个文件内容全部读取到数组中,但是我们又不知道要申请多大的空间,利用 刚刚介绍的方法就能轻松解决这个问题:

1
2
3
fseek(fp, 0, SEEK_END);	// 指向 文件末尾
long int len = ftell(fp); // 获取当前位置下标
rewind(fp); // 移动回文件开头,这是容易被忽的