一.什么是文件
磁盘上的文件是文件。
但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件
程序文件 :
包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀 为.exe).
数据文件 :
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件.
文件名 :
一个文件要有一个唯一的文件标识,以便用户识别和引用。
文件名包含3部分:文件路径+文件名主干+文件后缀
例如: c:\code\test.txt
为了方便起见,文件标识常被称为文件名。
二.文件的打开和关闭
(1).文件指针
文件指针 :
缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及 文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE. 例如,VS2008编译环境提供的 stdio.h 头文件中有以下的文件类型申明:
struct _iobuf
{
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。 每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节。
一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。 下面我们可以创建一个FILE*的指针变量:
FILE* pf; //文件指针变量
定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件.
(2).文件的打开和关闭
文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。
ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件.
FILE * fopen ( const char * filename, const char * mode );
int fclose ( FILE * stream );
打开方式如下:
文件使用方式 | 含义 | 如果指定文件不存在 |
---|---|---|
“r”(只读) | 为了输入数据,打开一个已经存在的文本文件 | 出错 |
“w”(只写) | 为了输出数据,打开一个文本文件 | 建立一个新的文件 |
“a”(追加) | 向文本文件尾添加数据 | 出错 |
“rb”(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
“wb”(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
“ab”(追加) | 向一个二进制文件尾添加数据 | 出错 |
“r+”(读写) | 为了读和写,打开一个文本文件 | 出错 |
“w+”(读写) | 为了读和写,建立一个文本文件 | 建立一个新的文件 |
“a+”(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新的文件 |
“rb+”(读写) | 为了读和写打开一个二进制文件 | 出错 |
“wb+”(读写) | 为了读和写,新建一个新的二进制文件 | 建立一个新的文件 |
“ab+”(读写) | 打开一个二进制文件,在文件尾进行读和写 | 建立一个新的文件 |
使用案例 :
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
FILE* ptr = fopen("data.txt", "r");
if (ptr == NULL)
{
printf("%s\n", strerror(errno));
return -1;
}
printf("打开文件成功\n");
fclose(ptr);
ptr = NULL;
}
三.文件的顺序读写
功能 | 函数名 | 适用于 |
---|---|---|
字符输入函数 | fgetc | 所有输入流 |
字符输出函数 | fputc | 所有输出流 |
文本行输入函数 | fgets | 所有输入流 |
文本行输出函数 | fputs | 所有输出流 |
格式化输入函数 | fscanf | 所有输入流 |
格式化输出函数 | fprint | 所有输出流 |
二进制输入 | fread | 文件 |
二进制输出 | fwrite | 文件 |
(1). fputc的使用
int fputc( int c, FILE *stream );
功能 : Writes a character to a stream or to stdout .
该函数返回写入的字符的ascii码值, 写入错误返回 EOF
使用 :
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return -1;
}
// 写入文件
int i = 'a';
for (i = 'a'; i <= 'z'; i++)
{
fputc(i, pf);
}
// 关闭文件
fclose(pf);
pf = NULL;
}
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return -1;
}
int i = 'a';
for (i = 'a'; i <= 'z'; i++)
{
fputc(i, stdout);
}
fclose(pf);
pf = NULL;
}
(2). fgetc的使用
int fgetc( FILE *stream );
功能 : Read a character from a stream or stdin .
该函数返回读取的字符的ascii码值, 读取错误返回 EOF
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return -1;
}
// 打开文件成功,读文件
int ch = 0;
while ((ch = fgetc(pf)) != EOF)
{
printf("%c\n", ch);
}
// 关闭文件
fclose(pf);
pf = NULL;
}
#include<stdio.h>
#include<string.h>
int main()
{
int ch = fgetc(stdin);
printf("%c\n", ch);
}
(3). fputs的使用
int fputs( const char *string, FILE *stream );
功能 : 如果成功,函数返回一个非负值。出错时,fputs返回EOF
函数将字符串复制到当前位置的输出流。
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return -1;
}
fputs("hello\n", pf); // pf也可改成 stdout
fputs("hello world\n", pf); // pf 也可改成 stdout
// 关闭文件
fclose(pf);
pf = NULL;
}
(4). fgets的使用
char *fgets( char *string, int n, FILE *stream );
功能 : fgets函数从输入流参数中读取字符串并将其存储在字符串中。fgets从当前流位置读取字符(包括第一个换行符),直到流结束,或者直到读取的字符数等于n–1,以先到者为准。以字符串形式存储的结果将附加一个空字符。换行符(如果已读取)包含在字符串中。
fgets类似于gets函数;但是,gets将换行符替换为NULL。
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
char arr[100];
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return -1;
}
// 读取文件
fgets(arr, 100, pf);
printf("%s\n", arr);
// 关闭文件
fclose(pf);
pf = NULL;
}
(5). fprintf的使用
int fprintf( FILE *stream, const char *format [, argument ]...);
功能 : Print formatted data to a stream.
fprintf返回写入的字节数, fprintf格式化并将一系列字符和值打印到输出流。每个函数参数(如果有)都会根据format中相应的格式规范进行转换和输出。对于fprintf,format参数的语法和用法与printf中的相同。
用例 :
#include<stdio.h>
#include<errno.h>
#include<string.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = {
"zhangsan",18,90.45f};
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return -1;
}
// 格式化写入
fprintf(pf, "%s %d %f", s.name, s.age, s.score);
// 关闭文件
fclose(pf);
pf = NULL;
}
(6). fscanf的使用
int fscanf( FILE *stream, const char *format [, argument ]... );
功能 : Read formatted data from a stream.
fscanf函数将数据从流的当前位置读取到参数(如果有的话)给定的位置。每个参数必须是指向与格式中的类型说明符对应的类型的变量的指针。format控制输入字段的解释,其形式和功能与scanf的format参数相同 , 返回从文件中读出的参数个数
使用 :
#include<stdio.h>
#include<errno.h>
#include<string.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = {
0 };
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return -1;
}
// 格式化读取
fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));
printf("%s %d %f", s.name, s.age, s.score);
// 关闭文件
fclose(pf);
pf = NULL;
}
(9). sprintf 和 sscanf 的使用
int sprintf( char *buffer, const char *format [, argument] ... );
int sscanf( const char *buffer, const char *format [, argument ] ... );
sprintf 功能 :
将格式化数据写入字符串。
sprintf函数在缓冲区中格式化并存储一系列字符和值。每个参数(如果有)都会根据format中相应的格式规范进行转换和输出。格式由普通字符组成,其形式和功能与printf的format参数相同。在写入的最后一个字符之后附加一个空字符。
sscanf 功能 :
从字符串读取格式化数据。
sscanf函数将数据从缓冲区读取到每个参数给定的位置。每个参数都必须是指向一个变量的指针,该变量的类型与格式中的类型说明符相对应。format参数控制输入字段的解释,其形式和功能与scanf函数的format参数相同
使用 :
#include<stdio.h>
#include<string.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = {
"zhangsan",18,94.6f};
char buf[200] = {
0 };
sprintf(buf, "%s %d %f", s.name, s.age, s.score);
printf("%s\n", buf);
struct S tmp = {
0 };
sscanf(buf, "%s %d %f", tmp.name, &(tmp.age), &(tmp.score));
printf("%s %d %f", tmp.name, tmp.age, tmp.score);
}
(10). fread 和 fwrite 的使用
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
fwrite 功能 :
将数据写入流。
buffer : 指向要写入的数据的指针
size : 数据大小(字节)
count : 要写入的最大数据数
stream : 指向文件结构的指针
fwrite函数从缓冲区向输出流最多写入count个数据
fread 功能 :
从流中读取数据。
fread函数从输入流中最多读取count个szie字节大小的项,并将它们存储在缓冲区中。
使用 :
#include<stdio.h>
#include<errno.h>
#include<string.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = {
"zhangsan",18,94.6f };
FILE* pf = fopen("data.txt", "wb");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return -1;
}
// 二进制写入
fwrite(&s, sizeof(s), 1, pf);
// 关闭文件
fclose(pf);
pf = NULL;
}
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
struct S s = {
0};
FILE* pf = fopen("data.txt", "rb");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return -1;
}
// 二进制读取
fread(&s, sizeof(s), 1, pf);
printf("%s %d %f\n", s.name, s.age, s.score);
// 关闭文件
fclose(pf);
pf = NULL;
}
四.文件的随机读写
(1). fseek ftell 和 rewind 的使用
int fseek( FILE *stream, long offset, int origin );
long ftell( FILE *stream );
void rewind( FILE *stream );
fseek 函数
功能 : 将文件指针移动到指定位置。
返回值 :
如果成功,fseek返回0。否则,它将返回一个非零值。
fseek函数将与流相关联的文件指针(如果有的话)移动到一个新位置,该位置是从源位置偏移字节的位置。流上的下一个操作发生在新位置。在为更新而打开的流上,下一个操作可以是读操作,也可以是写操作。参数origin必须是STDIO.H中定义的下列常量之一:
SEEK_CUR : 文件指针的当前位置
SEEK_END : 文件结尾
SEEK_SET : 文件开头
ftell 函数
long ftell( FILE *stream );
功能 : 获取文件指针的当前位置。
rewind 函数
void rewind( FILE *stream );
功能 : 将文件指针重新定位到文件的开头。
用例 :
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return -1;
}
// 读取文件
int ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
printf("%d\n", ftell(pf));
fseek(pf, -2, SEEK_CUR);
// rewind(pf); 和fseek产生的效果一致
ch = fgetc(pf);
printf("%c\n", ch);
fclose(pf);
pf = NULL;
}
五.文件读取结束的判定
ferror 和 feof 的使用
int ferror( FILE *stream );
int feof( FILE *stream );
ferror 函数
功能 : ferror函数测试与流相关的文件上的读写错误,如果流上没有发生错误,则ferror返回0。否则,它返回一个非零值。
feof 函数
功能 : feof 函数确定是否已到达流的结尾。feof函数在第一次读取操作后返回一个非零值,该操作试图读取文件的末尾。如果当前位置不是文件结尾,则返回0。
用例 :
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return -1;
}
int ch = 0;
while ((ch = fgetc(pf)) != EOF)
{
printf("%c\n", ch);
}
if (ferror(pf))
{
printf("读取时发生错误结束\n");
}
else if (feof(pf))
{
printf("遇到文件末尾结束\n");
}
fclose(pf);
pf = NULL;
}