目录
1. 为什么使用文件
2. 什么是文件
文件即指磁盘上的文件,在程序设计中,通常指两类文件:
1.程序文件
2.数据文件
3. 文件的打开和关闭
在C语言中,提供了两个函数分别用于文件的打开和关闭。
FILE *fopen( const char *filename, const char *mode );
参数一filename是文件名,可以用像tt.txt这样的相对路径(相对于当前工程文件的目录下),也可以用像W:\a\tt.txt这样的绝对路径(注意\前加\才能表示\字符),因为类型是字符指针,故文件名要加“ ”,表示字符串首地址。
FILE* pf = fopen("W:\\a\\tt.txt", "w");//绝对路径
FILE* pf = fopen("tt.txt", "w");//相对路径
参数二mode是指文件的打开方式,有多种打开方式,如:
该函数的返回值是FILE*类型的文件指针,事实上这是一种结构体指针,原因是文件被使用时在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个系统定义过的结构体变量中的。
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
不同编译器的FILE类型包含的内容不完全相同。
当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构体的变量,并初始化。这样一来,通过文件指针就能找到对应的文件。
当使用fopen函数打开文件失败时,函数会返回空指针。
于是可以这么使用:
FILE* pf = fopen("tt.txt", "w");
if (pf == NULL) {
perror("fopen->");
return 1;
}
fclose(pf);
pf = NULL;
4. 文件的顺序读写
常见读写的函数
fgetc函数可以从所有输入流(包括标准输入流(键盘))中返回字符的ASCII码值,读取后该文件指针会指向文件中下一个字符。
int fgetc ( FILE * stream );
如果读到文件末尾或者读取错误就会返回EOF,这个函数和getc等效,但是有些库getc是用宏实现的。
例如工程目录下在tt.txt,写字母a,然后用fgetc可以读取文件内容
fputc函数与这个函数相对,可以将字符输出到所有输出流中(包括标准输出流(屏幕)),输出后该文件指针会指向文件中下一个字符。
int fputc ( int character, FILE * stream );
如果写入成功,返回字符的ASCII码值,写入错误时返回EOF。
与fgetc,fputc对应的一组函数是fgets,fputs,不同的是输出和输入的是字符串,
char * fgets ( char * str, int num, FILE * stream );
从stream中读取num个字符(num包含了\0,实际就num-1个字符)拷贝到str指向的空间,在字符串结尾还会拷贝一个\0,直到遇到读到换行符或者文件结尾(EOF),如果读到换行符\n,会停止,把换行符也拷贝到str指向的空间。
fgets函数和gets不太一样,首先是参数个数,然后是gets不会拷贝\0到str中,gets不能指定拷贝的字符个数(可能会导致缓冲区溢出),而fgets可以指定拷贝个数。
使用实例:
以下实例证明确实拷贝了\0
以下两个函数,fscanf可以从所有输入流中(格式化)读取数据到文件中,二fprintf则可以从所有输出流格式化输出到文件中,使用过scanf和printf这组函数就明白了,只不过把屏幕换成了文件。 对比下函数参数易于学习。
int fscanf ( FILE * stream, const char * format, ... );
int scanf ( const char * format, ... );
int sscanf ( const char * s, const char * format, ...);
int fprintf ( FILE * stream, const char * format, ... );
int printf ( const char * format, ... );
int sprintf ( char * str, const char * format, ... );
sscanf函数可以从字符串中读取格式化数据,sprintf是把格式化数据写入到字符串中。
例如:
/* sscanf example */
#include <stdio.h>
int main ()
{
char sentence []="Rudolph is 12 years old";
char str [20];
int i;
sscanf (sentence,"%s %*s %d",str,&i);
printf ("%s -> %d\n",str,i);
return 0;
}
注:scanf类函数中添加了*的部分会被忽略(跳过),不会被读取,也就是说上面函数从sentence字符串中读取了Rudolph字符串到str中,然后跳过了字符串is,然后读取了12到i中。(在printf中使用,*表示用后面的形参替代*的位置,实现动态格式输出,如printf("%.*s\n", 1, "abc");从字符串“abc”输出宽度为1的字符串,结果是a)
输出如下:
注意:如果把%*s去掉则读不到12了,如下
如果调整参数顺序,结果如下:
5. 文件的随机读写
上述函数只能在文件上顺序读写数据,要实现在文件各个位置读写可以借助下列函数。
int fseek ( FILE * stream, long int offset, int origin );
这个函数可以复位文件指针,调用函数便可以让它文件指针stream指向指定的位置。
参数origin是起始位置,可以是这些参数:
offset是相对起始位置的偏移量(字节数)。
使用实例:
要得到文件指针的当前位置可以借助ftell函数
long int ftell ( FILE * stream );
该函数返回当前文件指针相对文件起始位置的偏移量(字节数)。
使用实例:
rewind函数,可以将文件指针重新指向文件的起始位置 void rewind ( FILE * stream );
例如:(文件指针指向开头,所以ftell返回0 )
6. 文本文件和二进制文件
数据文件被分成了文本文件和二进制文件两种类型。
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
通常,ptr是数组名,size是数组元素的大小(所占字节数),count是输出的元素个数,于是可以这么用,例如:
以二进制的形式打开,会发现文件中是这样的数据:
7. 文件读取结束的判定
需要注意不能用feof的返回值判断文件是否读取结束和直接判断文件是否为空。
int feof ( FILE * stream );
如果文件指针指向的位置已经没有数据了返回非0,表文件结束,否则返回0,表示还有数据。