1. 需要了解的概念
包括:数据流、缓冲区、文件类型、文件存取方式

1.1 数据流

指程序与数据的交互是以流的形式进行的。进行 C 语言文件的存取时,都会先进行 “打开文件” 操作,这个操作就是在打开数据流,而 “关闭文件” 操作就是关闭数据流。

1.2 ** 缓冲区 (Buffer)**:

指在程序执行时,所提供的额外内存,可用来暂时存放做准备执行的数据。它的设置是为了提高存取效率,因为内存的存取速度比磁盘驱动器快得多。

C 语言中带缓冲区的文件处理:

C 语言的文件处理功能依据系统是否设置 “缓冲区” 分为两种:一种是设置缓冲区,另一种是不设置缓冲区。由于不设置缓冲区的文件处理方式,必须使用较低级的 I/O 函数 (包含在头文件 io.h 和 fcntl.h 中) 来直接对磁盘存取,这种方式的存取速度慢,并且由于不是 C 的标准函数,跨平台操作时容易出问题。

下面只介绍第一种处理方式,即设置缓冲区的文件处理方式:

当使用标准 I/O 函数 (包含在头文件 stdio.h 中) 时,系统会自动设置缓冲区,并通过数据流来读写文件。当进行文件读取时,不会直接对磁盘进行读取,而是先打开数据流,将磁盘上的文件信息拷贝到缓冲区内,然后程序再从缓冲区中读取所需数据。当写入文件时,并不会马上写入磁盘中,而是先写入缓冲区,只有在缓冲区已满或 “关闭文件” 时,才会将数据写入磁盘。如下图所示:

1.3 文件类型

分为文本文件和二进制文件两种。

文本文件是以字符编码的方式进行保存的。二进制文件将内存中数据原封不至文件中,适用于非字符为主的数据。如果以记事本打开,只会看到一堆乱码。

其实,除了文本文件外,所有的数据都可以算是二进制文件。二进制文件的优点在于存取速度快,占用空间小,以及可随机存取数据。

1.4 文件存取方式

包括顺序存取方式和随机存取方式两种。

顺序读取也就是从上往下,一笔一笔读取文件的内容。保存数据时,将数据附加在文件的末尾。这种存取方式常用于文本文件,而被存取的文件则称为顺序文件。

随机存取方式多半以二进制文件为主。它会以一个完整的单位来进行数据的读取和写入,通常以结构为单位。

2 文本文件操作

C 语言中主要通过标准 I/O 函数来对文本文件进行处理。相关的操作包括打开、读写、关闭与设置缓冲区。

相关的存取函数有:**fopen (), fclose (), fgetc (), fputc (), fgets (), fputs (), fprintf (), fscanf ()** 等。

2.1 打开文件

函数原型:FILE *fopen (const char *path, const char *mode);
说明:

path:指定文件位置

mode:打开模式 打开成功,fopen 返回文件指针,否则返回一个 NULL

c
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
int main()
{
FILE *fp;
if ((fp = fopen("a.txt", "r")) == NULL)
{
printf("a.txt open failed ! \n");
return 1;
}
return 0;
}

使用 fopen () 函数打开的文件会先将文件复制到缓冲区。注意:所下达的读取或写入动作,都是针对缓冲区进行存取而不是磁盘,只有当使用 fclose () 函数关闭文件时,缓冲区中的数据才会写入磁盘。

文件打开模式:

“r” 只读

从文件起始位置开始读。 注意:文件不存在时,打卡失败。

“r+” 读写(以读为主)

文件不存在时,打开失败。

文件存在时,如果没设置文件指针,文件指针指向文件首。

“w” 只写

文件不存在时,创建文件。

文件存在时,清空文件,文件指针指向文件首。

“w+” 写读(以写为主)

文件不存在时,创建文件。

文件存在时,清空文件,文件指针指向文件首

“a” 追加写(只写)

文件不存在时,创建文件。

文件存在时,文件指针指向文件尾。(在文件为追加,即用 fseek()函数无效)

“a+” 追加读写(读写)

文件不存在时,创建文件。

文件存在时:

写:追加写(文件指针设置无效)

读:先设置文件指针,写完之后文件指针在文件尾(最初,文件指针在文件首)

二进制文件模式: 二进制文件的模式与打开文本文件的含义是一样的,不同的是模式名称里面多一个字母’b’,以表示以二进制形式打开文件。即:rb(只读)、rb+(读写) wb(只写) wb+(读写) ab(追加,写) ab+(追加,读写)

2.2 关闭文件

函数原型:*int fclose(FILE fp);

说明:关闭成功返回值 0,否则返回 EOF

注:在执行完文件的操作后,要进行 “关闭文件” 操作。虽然程序在结束前会自动关闭所有的打开文件,但文件打开过多会导致系统运行缓慢,这时就要自行手动关闭不再使用的文件,来提高系统整体的执行效率。

下面为文件的打开关闭:

c
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
int main()
{
FILE *fp;
if ((fp = fopen("a.txt", "r")) == NULL)
{
printf("a.txt open failed ! \n");
return 1;
}
fclose(fp);
return 0;
}

2.3 字符存取函数

函数原型:

*int fputc(int c, FILE stream);

*int fgetc(FILE stream);

*int feof(FILE stream);

fgetc():

可从文件数据流中一次读取一个字符,然后读取光标移动到下一个字符,并逐步将文件的内容读出。

如果字符读取成功,则返回所读取的字符,否则返回 EOF (end of file)。

EOF 是表示数据结尾的常量,真值为 - 1。另外,要判断文件是否读取完毕,可利用 feof () 进行检查。未完返回 0,已完返回非零值。

fgetc () 函数的使用

版本 1:利用 feof () 函数检查文件是否读取完毕

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
int main()
{
FILE *fp;
fp = fopen("c:\\a.txt", "r");
if (fp != NULL)
{
while (!feof(fp))
printf("%c", fgetc(fp));
}
else
{
printf("file open failed! \n");
}
fclose(fp);
return 0;
}

版本 2:利用文件结束标志 EOF (即 - 1)

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
main()
{
char ch;
FILE *fp;
fp = fopen("c:\\a.txt", "r");
if (fp != NULL)
{
ch = fgetc(fp);
while (ch != EOF)
{
printf("%c", ch);
ch = fgetc(fp);
}
}
else
{
printf("file open failed! \n");
}
fclose(fp);
return 0;
}

版本 3 - 重构版本 2

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
main()
{
char ch;
FILE *fp;
if ((fp = fopen("c:\\a.txt", "r")) != NULL)
{
while ((ch = fgetc(fp)) != EOF)
printf("%c", ch);
}
else
{
printf("file open failed! \n");
}
fclose(fp);
return 0;
}

fputc () 函数的使用

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
main()
{
FILE *fp;
if ((fp = fopen("a.txt", "w")) == NULL)
{
printf("a.txt open failed ! \n");
return 1;
}
int i;
for (i = 0; i < 10; i++)
{
putc('w', fp); //写10个'w'到a.txt文件中
}
fclose(fp);
return 0;
}

2.4 字符串存取函数

函数原型:

int fputs(const char *s, FILE *stream);

*char *fgets(char *s, int size, FILE stream);

说明:

fgets 函数的作用是从指定文件读入一个字符串,如:fgets (str, size, fp);

参数 size 为要求得到的字符个数,但只从 fp 指向的文件输入 n-1 个字符,然后在最后加一个’\0’字符,因此得到的字符串共有 n 个字符,把它们放在字符数组 str 中。

如果在读完 n-1 个字符之前遇到换行符或 EOF,读入结束。

fputs 函数的作用是向指定文件输出一个字符串,如:fputs (“Hey”, fp); 把字符串”Hey” 输出到 fp 指向的文件。fputs 函数的第一个参数可以是字符串常量、字符数组名或字符型指针。若输出成功,则返回 1,否则返回 EOF。

2.5 格式化存取函数

函数原型:

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

int fscanf(FILE *stream, const char *format, …);

说明:

它们与 printf 和 scanf 函数相仿,都是格式化读写函数。

不同的是:fprintf 和 fscanf 函数的读写对象不是终端 (标准输入输出),而是磁盘文件。

printf 函数是将内容输出到终端 (屏幕),因此,fprintf 就是将内容输出到磁盘文件了。

2.6 指针重返函数 rewind

函数原型:

*void rewind(FILE stream);

说明:

rewind 函数的作用是使位置指针重返回文件的开头,属于文件的定位。

等价于: (void) fseek (stream, 0L, SEEK_SET)

2.7 文件指针设置函数 fseek

函数原型:

*int fseek(FILE stream, long offset, intstart);

说明: 对流式文件可以进行顺序读写,也可以进行随机读写。

关键在于控制文件的位置指针,如果位置指针是按字节位置顺序移动的,就是顺序读写。

如果能将位置指针按需要移动到任意位置,就可以实现随机读写。

所谓随机读写,是指读完上一个字符 (字节) 后,并不一定要读写其后续的字符 (字节),而可以读写文件中任意位置上所需要的字符 (字节)。

start:起始点(用 0、1、2 代替)

0 代表文件开始,名字为 SEEK_SET,

1 代表当前位置,名字为 SEEK_CUR,

2 代表文件末尾,名字为 SEEK_END。

fseek () 函数一般用于二进制文件,因为文本文件要发生字符转换,计算位置时往往会发生混乱。

2.8 获得文件指针位置 ftell

函数原型: *long ftell(FILE stream);

说明: 获得当前文件指针位置

3.** 二进制文件操作 **

3.1 数据块存取函数

函数原型:

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);

说明:

ptr:对于 fread 来说,指的是读入数据的存放地址;对于 fwrite 来说,是要输出数据的地址。

size:读写数据时,每笔数据的大小

nmemb:读写数据的笔数

fp:文件指针

fwrite 函数

c
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
int main()
{
int str[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
FILE *fp;
if ((fp = fopen("a.txt", "w")) == NULL)
{
printf("file open failed!\n");
}
fwrite(str, sizeof(int), 10, fp);
fclose(fp);
return 0;
}

fread 函数

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
int main()
{
int str[10];
FILE *fp;
if ((fp = fopen("a.txt", "r")) == NULL)
{
printf("file open failed!\n");
}
fread(str, sizeof(int), 10, fp);
int i;
for (i = 0; i < 10; i++)
{
printf("%d ", str[i]);
}
printf("\n");
fclose(fp);
return 0;
}

4 其他 (ungetc,getchar ,putchar,printf,scanf,sprintf,sscanf)

函数原型:

**int ungetc(int c, FILE *stream);

int getchar(void); int putchar(int c);

int scanf(const char *format, …);

int sscanf(const char *str, const char *format, …);

int printf(const char *format, …);

int sprintf(char *str, const char *format, …);

int snprintf(char *str, size_t size, const char format, …);*

说明:

ungetc: 将刚从流中读入的字符放回到流。(注意:只能回放一个)

getchar () 等价于 getc (stdin) 即,从键盘获取字符

putchar () 等价于 putc (c, stdout) 即,向显示器写字符

scanf () 标准输入

sscanf (): 用法与 scanf 类似,只是从指定 string 读取

printf (): 标准输出

sprintf (): 用法与 printf 类似,只是写入的是指定 string,可以把任何数据转换成你想要的字符串格式

*int feof(FILE stream);

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
int main(void)
{
FILE *stream;
/*openafileforreading*/
stream = fopen("DUMMY.FIL", "r");
/*readacharacterfromthefile*/
fgetc(stream);
/*checkforEOF*/
if (feof(stream))
printf("We have reached the end of file\n");
/*closethefile*/
fclose(stream);
return 0;
}

feof (fp) 有两个返回值:如果遇到文件结束,函数 feof(fp)的值为非零值,否则为 0。

EOF 是文本文件结束的标志。在文本文件中,数据是以字符的 ASCⅡ 代码值的形式存放,普通字符的 ASCⅡ 代码的范围是 32 到 127(十进制),EOF 的 16 进制代码为 0xFF(十进制为 - 1),因此可以用 EOF 作为文件结束标志。[2] 当把数据以二进制形式存放到文件中时,就会有 - 1 值的出现,因此不能采用 EOF 作为二进制文件的结束标志。

为解决这一个问题,ASCI C 提供一个 feof 函数,用来判断文件是否结束。feof 函数既可用以判断二进制文件又可用以判断文本文件。

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
int a;
char b, c[100];
int main()
{
FILE *fp1 = fopen("input.txt", "r"); //打开输入文件
FILE *fp2 = fopen("output.txt", "w"); //打开输出文件
if (fp1 == NULL || fp2 == NULL) //若打开文件失败则退出
{
puts("不能打开文件!");
return 0;
}
fscanf(fp1, "%d", &a); //从输入文件读取一个整数
b = fgetc(fp1); //从输入文件读取一个字符
fgets(c, 100, fp1); //从输入文件读取一行字符串
printf("%ld", ftell(fp1)); //输出fp1指针当前位置相对于文件首的偏移字节数
fputs(c, fp2); //向输出文件写入一行字符串
fputc(b, fp2); //向输出文件写入一个字符
fprintf(fp2, "%d", a); //向输出文件写入一个整数
fclose(fp1); //关闭输入文件
fclose(fp2); //关闭输出文件,相当于保存
return 0;
}

**strlen ()** 函数可以直接求出字符串的长度 追加内容

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp = fopen("1.txt", "a+");
if (fp == 0)
{
printf("can't open file\n");
return 0;
}
fseek(fp, 0, SEEK_END);
char sz_add[] = "hello world\n";
fwrite(sz_add, strlen(sz_add), 1, fp);
fclose(fp);
return 0;
}

rewind(steam)

C 程序中的库函数,功能是将文件内部的指针重新指向一个流的开头

C 中的 fseek 函数使用 :C 中的 fseek 函数使用

整合自:
1、c 语言文件及其读写详解
2、【C 语言】文件常用读写操作(含读取学生信息示例)