在打开一个文件的时候,会创建一个文件信息区,而文件指针指向的内容就是文件信息区。
文件信息区中存储的到底是什么内容的,我们可以在VS2013中查看一下文件信息区的内容(不同编译器下有所差异)。
- struct _iobuf {
- char* _ptr;
- int _cnt;
- char* _base;
- int _flag;
- int _file;
- int _charbuf;
- int _bufsiz;
- char* _tmpfname;
- };
- typedef struct _iobuf FILE;
文件指针pf的类型是FILE*:
FILE* pf;//文件指针
文件的打开和关闭我们都知道怎么操作,但是如果在编程语言中提到打开关闭文件,我们可能就一无所知了。
下面介绍两个函数:
- //打开文件的函数fopen
- FILE * fopen ( const char * filename, const char * mode );
- //关闭文件的函数fclose
- int fclose ( FILE * stream );
fopen中有一个参数mode,这个参数其实对应的是打开方式;打开文件有很多种方式,比较常用的只有三种:
文件使用方式 | 含义 | 如果指定的文件不存在 |
---|---|---|
r(只读) | 读取一个已经存在的文本文件 | 出错 |
w(只写) | 打开一个文本文件,输出数据,打开文件之前会先清空文件内容 | 建立新文件 |
a (追加) | 向文本文件末尾添加数据,原来文件中的数据保留,新的数据添加到文件为,原文件EOF保留 | 建立新文件 |
演示fopen和fclose的使用:
- int main()
- {
- //打开文件
- FILE* pf = fopen("test.txt", "w");
-
- //关闭文件
- fclose(pf);
- pf = NULL;
- return 0;
- }
打开文件的目的是对文件进行读写,四个常用的文件读写函数:
功能 | 函数名 | 适用于 |
字符输入函数 | fgetc | 所有输入流 |
字符输出函数 | fputc | 所有输出流 |
文本行输入函数 | fgets | 所有输入流 |
文本行输出函数 | fputs | 所有输出流 |
- int fputc( int c, FILE *stream );//Writes a character to a stream (fputc, fputwc) or to stdout (_fputchar, _fputwchar).
-
fopen创建的文件默认在当前路径下;
- void test_fputc()
- {
- FILE* pf = fopen("test.txt", "w");
- //fputc的使用 两个参数-第一个为输出到文件的字符,第二个为输出的文件流
- for (int ch = 'a'; ch <= 'z'; ch++)
- {
- fputc(ch, pf);//输出26个英文字母
- }
- fclose(pf);
- }
fputc每次只能输出单个字符,想要输出多个字符需要借助循环。
int fgetc( FILE *stream );
fputc是把字符输出到文件,fgetc则是把文件中的内容读出来;从用法上来说fgetc的使用更加简单。
- void test_fgetc()
- {
- FILE* pf = fopen("test.txt", "r");//读取test.txt中的内容
-
- //使用fgetc读数据
- char ch;
- while((ch = fgetc(pf)) != EOF)
- {
- printf("%c ", ch);
- }
- fclose(pf);
- }
使用fgetc也是可以读取成功的,而且每次读取之后文件指针也是会向后移动的。
单个字符的输入和输出会很麻烦,那么可不可以以字符串的形式输出和输入呢?
答案是可以的,那就是使用fputs和fgets。
int fputs( const char *string, FILE *stream );
fputs在使用上还是很简单的:
- void test_fputs()
- {
- FILE* pf = fopen("mytest.txt", "w");
- //使用fputs将字符输出到文件中 两个参数-第一个为常量字符串,第二个为输出的文件流指针
- fputs("this is a test\n", pf);
- fclose(pf);
- }
fgets和fputs作用相反,fgets用来读取文件中的数据到程序中:
- char *fgets( char *string, int n, FILE *stream );
- //fgets有三个参数,string为读取到的内存区域,n表示读取的最大字符个数,stream为文件指针
- void test_fgets()
- {
- FILE* pf = fopen("mytest.txt", "r");//读数据
- //fgets
- char buf[101] = { 0 };
- printf("%s\n", fgets(buf, 101, pf));
-
- fclose(pf);
- }
函数的参数:
scanf: int scanf( const char *format [,argument]... );
fscanf: int fscanf( FILE *stream, const char *format [, argument ]... );
sscanf: int sscanf( const char *buffer, const char *format [, argument ] ... );
针对的输入流:
scanf :格式化的输入函数
fscanf :所有输入流
sscanf:把一个字符串转换成格式化的数据
sscanf使用示例:
- struct S
- {
- char name[20];
- int age;
- double grade;
- };
-
- int main()
- {
- char buf[256] = { 0 };
- struct S tmp = { 0 };
- struct S s = { "zhangsan", 50, 50.8 };
- sprintf(buf, "%s %d %lf", s.name, s.age, s.grade);//把结构体中的数据转化为字符串
-
- //从buf中提取一个结构体对象
- sscanf(buf, "%s %d %lf", tmp.name, &(tmp.age), &(tmp.grade));
- printf("%s %d %f", tmp.name, tmp.age, tmp.grade);
- return 0;
- }
函数参数:
printf: int printf( const char *format [, argument]... );
fprintf: int fprintf( FILE *stream, const char *format [, argument ]...);
sprintf: int sprintf( char *buffer, const char *format [, argument] ... );
针对的输出流:
printf : 格式化的输出函数
fprintf : 针对所有输出流的格式化输出函数
sprintf : 把一个格式化的数据转化成字符串
fprintf使用示例:
- struct S
- {
- char name[20];
- int age;
- double grade;
- };
-
- int main()
- {
- struct S s = { "张三", 50, 50.8 };
- FILE* pf = fopen("test.txt", "w");
- fprintf(pf, "%s %d %lf", s.name, s.age, s.grade);
- fclose(pf);
- pf = NULL;
- return 0;
- }
sprintf使用示例:
- struct S
- {
- char name[20];
- int age;
- double grade;
- };
-
- int main()
- {
- char buf[256] = { 0 };
- struct S s = { "zhangsan", 50, 50.8 };
- sprintf(buf, "%s %d %lf", s.name, s.age, s.grade);
- printf("%s\n", buf);
- return 0;
- }
int fseek( FILE *stream, long offset, int origin );
fseek是根据文件指针的偏移量来进行随机读写。
origin的三种情况:
SEEK_CUR:文件指针的当前位置
SEEK_END:文件结束位置
SEEK_SET:文件开始位置
若origin使用SEEK_CUR,offset不能为负数;若origin使用SEEK_END,offset不能为正。
- void test_fseek()
- {
- FILE* pFile = fopen("example.txt", "w");
-
- //写文件
- fputs("This is an apple.", pFile);
- //偏移
- fseek(pFile, 9, SEEK_SET);
- //关闭文件
- fputs(" sam", pFile);
-
- fclose(pFile);
- pFile = NULL;
- }
下面解释一下为什么是这个结果:
返回文件指针相对于起始位置的偏移量,返回类型为long。
long int ftell ( FILE * stream );
示例:
- void test_ftell()
- {
- //打开文件
- FILE* pf = fopen("test.txt", "w");
-
- //随机读
- fputc('a', pf);
- fputc('b', pf);
-
- long pos = ftell(pf);
- printf("%d\n", pos);
-
- //关闭文件
- fclose(pf);
- pf = NULL;
- }
让文件指针的位置回到文件的起始位置。
void rewind ( FILE * stream );
示例:
- int main()
- {
- FILE* pf = fopen("test.txt", "w");
-
- fputc('a', pf);
- fputc('b', pf);
-
- long pos = ftell(pf);//2
- printf("%d\n", pos);
-
- rewind(pf);
- pos = ftell(pf);
- printf("%ld", pos);//回到起始位置0
-
- fclose(pf);
- pf = NULL;
- return 0;
- }
总结:文件操作的内容和之前的基础内容相比还是有难度的,主要是一些不常见的函数需要学习使用;然后文件操作部分的代码还是需要自己手动去敲的,不然一段时间之后,再回顾这些内容的时候就会发现已经忘得七七八八了;最后有时间的话可以总结成博客,在复习的时候借助自己的博客是更高效的。