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
#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+” 追加读写(读写)
文件不存在时,创建文件。
文件存在时:
写:追加写(文件指针设置无效)
读:先设置文件指针,写完之后文件指针在文件尾(最初,文件指针在文件首)
DESCRIPTION
r Open text file for reading. The stream is positioned at the beginning of the file.
r+ Open for reading and writing. The stream is positioned at the beginning of the file.
w Truncate file to zero length or create text file for writing. The stream is positioned at the beginning of the file.
w+ Open for reading and writing. The file is created if it does not exist, otherwise it is truncated.
The stream is positioned at the beginning of the file.
a Open for appending (writing at end of file). The file is created if it does not exist. The stream is positioned at the end of the file.
a+ Open for reading and appending (writing at end of file). The file is created if it does not exist.
The initial file position for reading is at the beginning of the file, but output is always appended to the end of the file.
二进制文件模式:
二进制文件的模式与打开文本文件的含义是一样的,不同的是模式名称里面多一个字母'b’,以表示以二进制形式打开文件。即:rb(只读)、rb+(读写) wb(只写) wb+(读写) ab(追加,写) ab+(追加,读写)
2.2 关闭文件
函数原型:int fclose(FILE *fp);
说明:关闭成功返回值0,否则返回EOF
注:在执行完文件的操作后,要进行“关闭文件”操作。虽然程序在结束前会自动关闭所有的打开文件,但文件打开过多会导致系统运行缓慢,这时就要自行手动关闭不再使用的文件,来提高系统整体的执行效率。
下面为文件的打开关闭:
#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()函数检查文件是否读取完毕
#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)
#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
#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()函数的使用
#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++){
fputc('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, int start);
说明:
对流式文件可以进行顺序读写,也可以进行随机读写。
关键在于控制文件的位置指针,如果位置指针是按字节位置顺序移动的,就是顺序读写。
如果能将位置指针按需要移动到任意位置,就可以实现随机读写。
所谓随机读写,是指读完上一个字符(字节)后,并不一定要读写其后续的字符(字节),而可以读写文件中任意位置上所需要的字符(字节)。
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函数:
#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函数:
#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,可以把任何数据转换成你想要的字符串格式