C08-files
本文最后更新于:1 小时前
C文件读写
打开文件
使用 <stdio.h> 头文件中的 fopen() 函数来创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE 的一个对象,类型 FILE 包含了所有用来控制流的必要的信息。下面是这个函数调用的原型:
FILE *fopen(char *filename, char *mode);
filename为文件名(包括文件路径),mode为访问模式,它们都是字符串。
文件也有不同的类型,按照数据的存储方式可以分为二进制文件和文本文件,它们的操作细节是不同的。最基本的文件打开方式有以下几种:
|控制读写权限的字符串(必须指明)|
模式 | 说明 |
---|---|
“r” | 以“只读”方式打开文件。只允许读取,不允许写入。文件必须存在,否则打开失败。 |
“w” | 以“写入”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。 |
“a” | 以“追加”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。 |
“r+” | 以“读写”方式打开文件。既可以读取也可以写入,也就是随意更新文件。文件必须存在,否则打开失败。 |
“w+” | 以“写入/更新”方式打开文件,相当于w和r+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。 |
“a+” | 以“追加/更新”方式打开文件,相当于a和r+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。 |
控制读写方式的字符串(可以不写) | |
打开方式 | 说明 |
“t” | 文本文件。如果不写,默认为”t”。 |
“b” | 二进制文件。 |
读写权限和读写方式可以组合使用,但是必须将读写方式放在读写权限的中间或者尾部(换句话说,不能将读写方式放在读写权限的开头)。例如:
将读写方式放在读写权限的末尾:”rb”、”wt”、”ab”、”r+b”、”w+t”、”a+t”
将读写方式放在读写权限的中间:”rb+”、”wt+”、”ab+”
其中r+和w+看似一样,都是读写,其实还是有几点区别的:
1.模式r+找不到文件不会自动新建,而w+会;
2.模式r+打开文件后,不会清除文件原数据,若直接开始写入,只会从起始位置开始进行覆盖,而w+会直接清零后,再开始读写;
模式的合法性说明:不能用大写,只能是小写,且rb+和r+b都是合法的,但br+和+rb等都是非法的,w和a也是一样的处理;
模式w的自动新建文件是有条件的,只有对应的路径存在(即文件所在的文件夹存在),文件不存在才会新建,否则是不会新建的,返回NULL
整体来说,文件打开方式由 r、w、a、t、b、+ 六个字符拼成,各字符的含义是:
r(read):读;w(write):写;a(append):追加;t(text):文本文件;b(binary):二进制文件;+:读和写
fopen() 会获取文件信息,包括文件名、文件状态、当前读写位置等,并将这些信息保存到一个 FILE 类型的结构体变量中,然后将该变量的地址返回。
FILE 是 <stdio.h> 头文件中的一个结构体,它专门用来保存文件信息。
如果希望接收 fopen() 的返回值,就需要定义一个 FILE 类型的指针。例如:FILE *fp = fopen("demo.txt", "r");
表示以“只读”方式打开当前目录下的 demo.txt 文件,并使 fp 指向该文件,这样就可以通过 fp 来操作 demo.txt 了。fp 通常被称为文件指针。
判断文件是否打开成功
打开文件出错时,fopen() 将返回一个空指针,也就是 NULL,我们可以利用这一点来判断文件是否打开成功,请看下面的代码:
1 |
|
我们通过判断 fopen() 的返回值是否和 NULL 相等来判断是否打开失败:如果 fopen() 的返回值为 NULL,那么 fp 的值也为 NULL,此时 if 的判断条件成立,表示文件打开失败。
关闭文件
文件一旦使用完毕,应该用 fclose() 函数把文件关闭,以释放相关资源,避免数据丢失。这个函数实际上会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。
fclose() 的用法为:int fclose(FILE *fp);
fp 为文件指针。例如:fclose(fp);
文件正常关闭时,fclose() 的返回值为0,如果关闭文件时发生错误,函数返回 EOF。
EOF 是 end of file 的缩写,表示文件末尾,是一个定义在头文件 stdio.h 中的宏,它的值是一个负数,往往是 -1。EOF 不绝对是 -1,也可以是其他负数,这要看编译器的实现。
读取&写入文件
fgetc和fputc函数用法详解(以字符形式读写文件)
以字符形式读写文件时,每次可以从文件中读取一个字符,或者向文件中写入一个字符。主要使用两个函数,分别是 fgetc() 和 fputc()。
fgetc() 的用法为:
int fgetc (FILE *fp);
fp 为文件指针。fgetc() 读取成功时返回读取到的字符,读取到文件末尾或读取失败时返回EOF。
举例:
1 |
|
表示从D:\demo.txt文件中读取一个字符,并保存到变量 ch 中。
在文件内部有一个位置指针,用来指向当前读写到的位置,也就是读写到第几个字节。在文件打开时,该指针总是指向文件的第一个字节。使用 fgetc() 函数后,该指针会向后移动一个字节,所以可以连续多次使用 fgetc() 读取多个字符。
fputc() 的用法为:
int fputc ( int ch, FILE *fp );
ch 为要写入的字符,fp 为文件指针。fputc() 写入成功时返回写入的字符,失败时返回 EOF,返回值类型为 int 也是为了容纳这个负数。例如:
fputc('a', fp);
或者char ch = 'a';fputc(ch, fp);
表示把字符 ‘a’ 写入fp所指向的文件中。
两点说明
被写入的文件可以用写、读写、追加方式打开,用写或读写方式打开一个已存在的文件时将清除原有的文件内容,并将写入的字符放在文件开头。如需保留原有文件内容,并把写入的字符放在文件末尾,就必须以追加方式打开文件。不管以何种方式打开,被写入的文件若不存在时则创建该文件。
每写入一个字符,文件内部位置指针向后移动一个字节。
判断文件读取完毕还是读取出错
feof() 函数用来判断文件内部指针是否指向了文件末尾,它的原型是:
int feof ( FILE * fp );
当指向文件末尾时返回非零值,否则返回零值。
ferror() 函数用来判断文件操作是否出错,它的原型是:
int ferror ( FILE *fp );
出错时返回非零值,否则返回零值。
fgets和fputs函数的用法详解(以字符串的形式读写文件)
fgets() 函数用来从指定的文件中读取一个字符串,并保存到字符数组中,它的用法为:
char *fgets ( char *str, int n, FILE *fp );
str 为字符数组,n 为要读取的字符数目,fp 为文件指针。
返回值:读取成功时返回字符数组首地址,也即 str;读取失败时返回 NULL;如果开始读取时文件内部指针已经指向了文件末尾,那么将读取不到任何字符,也返回 NULL。例如:
1 |
|
注意,读取到的字符串会在末尾自动添加 ‘\0’,n 个字符也包括 ‘\0’。也就是说,实际只读取到了 n-1 个字符,如果希望读取 100 个字符,n 的值应该为 101。
需要重点说明的是,在读取到 n-1 个字符之前如果出现了换行,或者读到了文件末尾,则读取结束。这就意味着,不管 n 的值多大,fgets() 最多只能读取一行数据,不能跨行。在C语言中,没有按行读取文件的函数,我们可以借助 fgets(),将 n 的值设置地足够大,每次就可以读取到一行数据。
fputs() 函数用来向指定的文件写入一个字符串,它的用法为:
int fputs(const char *str, FILE *fp );
str 为要写入的字符串,fp 为文件指针。写入成功返回非负数,失败返回 EOF。例如:
1 |
|
表示把字符串 str 写入到 D:\demo.txt 文件中。
fread和fwrite的用法详解(以数据块的形式读写文件,二进制 I/O 函数)
对于 Windows 系统,使用 fread() 和 fwrite() 时应该以二进制的形式打开文件,下面两个函数用于二进制输入和输出:
fread() 函数用来从指定文件中读取块数据。所谓块数据,也就是若干个字节的数据,可以是一个字符,可以是一个字符串,可以是多行数据,并没有什么限制。fread() 的原型为:
size_t fread ( void *ptr, size_t size, size_t nmemb, FILE *fp );
fwrite() 函数用来向文件中写入块数据,它的原型为:
size_t fwrite ( void * ptr, size_t size, size_t nmemb, FILE *fp );
对参数的说明:
ptr 为要写入的数据的头指针,它可以是数组、变量、结构体等。fread() 中的 ptr 用来存放读取到的数据,fwrite() 中的 ptr 用来存放要写入的数据。
size:表示要被写入的每个元素的大小,以字节为单位。
nmemb:表示要读写的数据元素的个数。
fp:表示文件指针,表示往哪里写。
理论上,每次读写 size*nmemb 个字节的数据。
size_t 是在 stdio.h 和 stdlib.h 头文件中使用 typedef 定义的数据类型,表示无符号整数,也即非负数,常用来表示数量。
返回值:返回成功读写的块数,也即 nmemb。如果返回值小于 nmemb:
对于 fwrite() 来说,肯定发生了写入错误,可以用 ferror() 函数检测。
对于 fread() 来说,可能读到了文件末尾,可能发生了错误,可以用 ferror() 或 feof() 检测。
数据写入完毕后,位置指针在文件的末尾,要想读取数据,必须将文件指针移动到文件开头,这要使用rewind(fp);函数。
实例
下面的实例演示了 fwrite() 函数的用法。
1 |
|
编译并运行上面的程序,这将创建一个文件 file.txt,它的内容如下:
This is runoob.com
下面的实例演示了 fread() 函数的用法。
1 |
|
编译并运行上面的程序,这将创建一个文件 file.txt,然后写入内容 This is runoob。接下来我们使用 fseek() 函数来重置写指针到文件的开头,文件内容如下所示:
This is runoob
fprintf和fscanf的用法详解(格式化读写文件)
fprintf函数它的原型是:
int fprintf (FILE* stream, const char*format, [argument])
该函数是一个格式化写入文件的库函数,可以看到,除了长得和printf函数很像以外,参数也非常像,多了一个第一个参数,文件指针,即第一步打开文件时得到的文件指针,后面的参数和printf一样,按照指定的格式将数据写入文件。
返回值
如果成功,则返回写入的字符总数,否则返回一个负数。
实例
1 |
|
编译并运行上面的程序,这将创建文件 file.txt,它的内容如下:
hello world 111
fscanf函数可以从文件里读数据,它的函数原型如下:
int fscanf(FILE *stream, char *format[,argument...]);
作为格式化写数据函数,它的参数同样比scanf也多一个参数,即第一个参数文件指针,表示读取的文件目标,其余参数和scanf一样,按照相应的格式进行读取,返回值表示读取数据的字节数。
返回值
如果成功,该函数返回成功匹配和赋值的个数。但是在遇到第一个空格和换行符时,它会停止读取。如果到达文件末尾或发生读错误,则返回 EOF。
实例
1 |
|
编译并运行上面的程序,这将产生以下结果:
1 |
|
rewind和fseek函数的用法详解(随机读写文件)
移动文件内部位置指针的函数主要有两个,即 rewind() 和 fseek()。
rewind() ,用于将文件指针重新指向文件的开头,同时清除和文件流相关的错误和eof标记,相当于调用fseek(stream, 0, SEEK_SET)
,它的原型为:
void rewind ( FILE *stream );
参数
stream 为指向 FILE 对象(已打开文件)的指针,该 FILE 对象标识流。
例如,把一个文件的内容显示在屏幕上,并同时复制到另一个文件。
1 |
|
fseek() 用来将位置指针移动到任意位置,它的原型为:
int fseek ( FILE *steam, long offset, int whence );
参数说明:
steam 为文件指针,也就是被移动的文件。
offset 为偏移量,也就是要移动的字节数。之所以为 long 类型,是希望移动的范围更大,能处理的文件更大。offset 为正时,向后移动;offset 为负时,向前移动。
whence 为起始位置,也就是从何处开始计算偏移量。C语言规定的起始位置有三种,分别为文件开头、当前位置和文件末尾,每个位置都用对应的常量来表示:
起始点 | 常量名 | 常量值 |
---|---|---|
文件开头 | SEEK_SET | 0 |
当前位置 | SEEK_CUR | 1 |
文件末尾 | SEEK_END | 2 |
例如,把位置指针移动到离文件开头100个字节处:
fseek(fp, 100, 0);
返回值:如果成功,则该函数返回零,否则返回非零值。
值得说明的是,fseek() 一般用于二进制文件,在文本文件中由于要进行转换,计算的位置有时会出错。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!