每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息。这些信息是保存在一个结构体变量中,该结构体类型是有系统声明的,,取名叫FILE。
不同的C编译器FILE类型包含的内容不完全相同,但都差不多。每当打开一个文件的时候,系统会自动创建一个FILE结构的变量,使用者不必担心。
文件在读写之前应该先打开文件,在使用结束之后应该关闭文件
ANSIC规定使用fopen函数来打开文件,fclose函数来关闭文件。
常用打开形式如下:
代码实现如下:
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE*fp //定义一个FILE类型的指针
if((fp=fopen("text","w"))==NULL) //打开一个文件名为“text”的文件并只写,如果没有则建立一个新文件“text”
{
printf("can't open the file!"); //打开失败就报错
exit(0);//退出程序
}
fclose(fp);//关闭文件
}
函数名 | 调用形式 | 功能 | 返回值 |
---|---|---|---|
fgetc | fgetc(fp) | 从fp指向的文件读入一个字符 | 读成功,带回所读的字符;失败则返回文件结束标志EOF(即-1) |
fputc | fputc(ch,fp) | 把字符ch写到fp所指的文件中 | 输出成功,返回值就是输出的字符;输出失败,则返回EOF |
fgetc()函数是指从指定文件中读取一个字符,读到文件末尾或者读取失败时返回EOF。
char fgetc(FILE*fp);
char a;
a=fgetc(FILE*fp); //从指定文件中得到一个字符给a
fputc()函数是向指定文件中写入一个字符
char fputc(int ch,FILE*fp);
ch为要写入的字符,fp为文件指针
实例代码如下
include<stdio.h>
include<stdlib.h>
int main()
{
char c;
FILE*fp;
if((fp=fopen("C:\\Users\\guan\\Desktop\\test.txt", "w"))==NULL)//文件采用绝对路径和书写的方式进行打开,若采用“w”打 开文件则文件若不存在则会退出程序
{
printf("ERROR");
exit(0);
}
c=fgetc(fp); //从fp指向的文件中获取一个字符给c
fputc('a',fp); //分别将'a','b','c'输入到fp指向的文件中
fputc('b',fp);
fputc('c',fp);
fputs("12516513\\n",fp);
fputs("scssb\\n",fp);
fclose(fp);//文件关闭
fp = NULL;//此时指针成为了野指针,需要置零
return 0;
}
函数名 | 调用形式 | 功能 | 返回值 |
---|---|---|---|
fgets | fets(str,n,fp) | 从fp指向的文件读入一个长度为(n-1)的字符串,存放到字符数组str中。 | 读成功,返回地址str,失败则返回NULL |
fputs | fputs(str,fp) | 把str所指向的字符串写到fp所指向的文件中 | 输出成功,返回0;否则返回非0值 |
从指定文件读入一段长度为n的字符串到字符串
char*fgets(char*buf,int n,FILE*fp);
buf为存储字符串的地址,n为读取字符串的长度,fp为文件的指针
注意!
该函数遇到 \n 停止,想读取多行只能重复进行操作
该函数最多只能读取一行,也就是说就算n赋值为10000,但文件中这一行只有一个字节,那也只会读取这一个字节
将一串字符串输入到指定文件中
int fputs(const char*str,FILE*fp);
str为要输入的字符串,fp为文件指针
同样的:也是遇到 \n 停止,写入多行需要重复进行操作
实例代码如下:
#include<stdio.h>
#include<stdlib.h>
int main()
{
char s[100] = { 0 };
FILE*fp;
if((fp=fopen("C:\\Users\\guan\\Desktop\\test.txt", "w"))==NULL)
{
printf("ERROR");
exit(0);
}
scanf("%s",s); //从终端输入一行字符串给s
fputs(s,fp); //将s输入到fp指向的文件中
fgets(s, 1000, fp); //从fp指向的文件中获取一行长度为1000的字符串给s
printf("%s", s); //打印s
return 0;
}
函数名 | 调用形式 |
---|---|
fscanf | fscanf(文件指针,格式字符串,输入列表) |
fprintf | fprintf(文件指针,格式字符串,输出列表) |
函数名 | 调用形式举例 | 功能 | 返回值 |
---|---|---|---|
fscanf | 例如:fscanf(fp,“%d %f”,&m,&n); | 从fp所指向的文件中,按“%d %f”的格式读取两个值,将这两个值分别存储在地址&m和&n对应的内存单元中。 | 若读取成功,返回值是读取的数据个数,此处为2;若读取失败,返回EOF(即-1) |
fprintf | 例如:fprintf(fp,“%d %f”,m,n); | 按“格式字符串”指定的格式,将“输出项列表”中指定的各项的值写入“文件类型指针”所指向的文件的当前位置。 | 若写入成功,返回值是写入文件的字符个数(或字节个数);若写入失败,返回EOF |
fscanf()和fprintf()函数读写对象不是键盘和显示器,而是磁盘文件,对文件进行格式化的输入输出。
但是在内存与磁盘交换数据频繁的情况下,最好不要用fscanf和fprintf函数,而用下面要介绍的fread和fwrite函数进行二进制的读写。
fscanf()是从指定文件(或者流)中读取数据,并根据参数格式将它们存储到附加参数所指向的位置。附加参数应指向格式字符串中的相应格式说明符指定的类型已分配的对象
int fscanf(FILE*fp,const char*format,...,char*buf,...);
fp为文件指针,format为参数格式,buf为输入列表的参数地址
实例代码如下:
struct S
{
int n;
double d;
};
int main()
{
struct S s = {0};
FILE*fp;
if((fp=fopen("C:\\Users\\guan\\Desktop\\test.txt", "w"))==NULL)
{
printf("ERROR");
exit(0);
}
//读文件r
fscanf(fp, "%d %lf", &s.n, &s.d);
printf("%d %lf\n", s.n, s.d);
//关闭文件
fclose(pf);
fp = NULL;
}
fprintf()是把格式字符串输出都指定文件中,所以参数比printf多一个文件指针FILE*。
int fprintf(FILE*fp,const char*format,...,[argument]);
fp为指定文件,format为参数格式,argument为输出列表
实例代码如下:
struct s
{
char name[20];
int age;
float hight;
};
int main()
{
struct s p1 = { "4516451",123,18885.4 };
FILE*fp;
if((fp=fopen("C:\\Users\\guan\\Desktop\\test.txt", "w"))==NULL)
{
printf("ERROR");
exit(0);
}
fprintf(fp, "%s,%d,%f", p1.name, p1.age, p1.hight);
fclose(fp);
fp = NULL;
return 0;
}
函数名 | 调用形式 | 功能 | 返回值 |
---|---|---|---|
fread | fread(buf,size,count,fp) | 以二进制的方式从fp所指向的文件中读取count条长度为size的数据(总共size*count个字节)到地址buf对应的内存单元 | 若读取成功,返回值是count;出错或读到文件末尾时返回值可能小于count,也可能是0。要用feof或ferror函数来判断出错情况。 |
fwrite | fwrite(buf,size,count,fp) | 将地址buf对应的内存单元中count条长度为size的数据(总共size*count个字节)以二进制的方式写入fp所指向的文件中 | 若读取成功,返回值是count;若失败返回值可能小于count,也可能是0。要用feof或ferror函数来判断出错情况。 |
C语言允许用fread函数从文件中读取一个数据块,用fwrite函数向文件写一个数据块。在读写时是以二进制形式进行的。
size_t fread(void* buf,size_t size,size_t count,FILE* fp);//fread
size_t fwrite(const void* buf,size_t size,size_t count,FILE* fp);//fwrite
实例代码如下:
struct s
{
char name[20];
int age;
int grade;
};
int main()
{
FILE* p = fopen("C:\\Users\\guan\\Desktop\\test.txt", "wb");
struct s p1 = { "513",12,13 };
if (p == NULL)
{
printf("ERROR");
exit(0);
}
fwrite(&p1, sizeof(p1), 1, p); //将p1中1个长度刚刚好为1个结构体p1的长度的结构体数据写入p指向的文件中
fclose(p);
p = NULL;
return 0;
}
int main()
{
FILE* p = fopen("C:\\Users\\guan\\Desktop\\test.txt", "rb");
struct s p1 = { 0 };
if (p == NULL)
{
printf("ERROR");
exit(0);
}
fread(&p1, sizeof(p1), 1, p); //从p指向的文件中读取1个长度刚刚好为1个结构体p1的长度的结构体数据给p1
printf("%s,%d,%d", p1.name, p1.grade, p1.age);
fclose(p);
p = NULL;
return 0;
}
文件位置标记:如果是顺序读写文件,则每读(或者写)完一个数据后,文件位置标记会向后移一个位置,然后在下一次执行操作时把数据读(或者写)入指针所指的位置。直到把全部数据写完,此时文件位置标记在最后一个数据的后面。
随机读写:根据人为的需要,移动文件位置标记的位置。文件位置标记可以向前移,向后移,或者移到文件头,文件尾,然后对该位置进行读写,显然这就不是顺序读写,而是随机读写了。
int fseek(FILE*fp,long int offset,int origin);
fp为文件指针,offest为偏移量(正数表示向右偏移,负数表示向左偏移),origin为用参数 设定从文件的哪里开始偏移,可能取值:
在C语言中,随机读写方式例如fseek**通常用于二进制文件,而不是文本文件**。这是因为文本文件通常是按顺序排列的字符流,而二进制文件可以具有不同的结构。
SEEK_CUR、SEK_END、SEEK_SET。
位移量 指以起始点为基点,向前移动的字节数。位移量应该是long型数据(在数字的末尾加一个字母L,就表示long型数据)。
代码实例如下:
#include<stdio.h>
#include<string.h>
int main()
{
FILE* fp;
char s[100];
fp = fopen("kkk", "wb");
fputs("This is an apple", fp);
fclose(fp);
fp = fopen("kkk", "rb");
fseek(fp, 7, 0); //将文件位置标记从文件开头右移7个位置,此处右移到了an前面的空格
fgets(s, 1000, fp);
printf("%s", s); //输出为“ an apple”
fclose(fp);
return 0;
}
void rewind(FILE*fp);
将文件位置标记移到文件开头
可用于非二进制文件
实例代码如下:
int main()
{
FILE*fp;
char s[100];
if((fp=fopen("ddd","w"))==NULL)
{
printf("ERROR");
exit(0);
}
scanf("%s",s);
fwrite(s,sizeof(s),1,fp); //写完之后文件位置标记到了最后一个数据之后
rewind(fp); //使文件位置标记返回文件头
fclose(fp);
return 0;
}
long int ftell(FILE*fp)
得到流式文件中文件位置标记的当前位置
实例代码如下:
i=ftell(fp);
if(i==-1L)printf("error\n"); //如果调用函数时出错(如不存在fp指向的文件),ftell函数返回值为-1L。
feof函数的判断方法不是判断当前指针是否指向文件末尾,而是根据上一次读取的返回值进行判断,如果上一次读取的返回值为EOF,那么feof(fp)为真,否则为假。
实例代码如下:
#include <stdio.h>
int main(){
FILE *fp = fopen("code.txt","r");
if (fp != NULL)
{
char ch = fgetc(fp);
while (!feof(fp)) //feof检测的是上一次读取的返回值
{
putchar(ch);
ch = fgetc(fp);
}
}
else
printf("fail to open!");
fclose(fp);
return 0;
}
函数名 | 调用形式 | 功能 | 返回值 |
---|---|---|---|
ferror | ferror(fp) | 检测调用各种输入输出函数(*fgetc、fread等等)*时是否出现了错误 | 如果未出错,返回值是0;如果出错了,返回值是一个非零值。 |
clearerr | clearerr(fp) | 使文件错误标志和文件结束标志置为0 | 无返回值 |
应该注意,对同一个文件每次调用输入输出函数,都会产生一个新的ferror函数值。因此,应当在调用一个输入输出函数后立即检查ferror函数的值,否则信息会在下一次调用输入输出函数时丢失
另外,当在执行fopen函数之后,ferror函数的初始值会自动置为0
假设在调用一个输入输出函数时出现错误,ferror函数值为一个非零值。使用完ferror函数之后,应立即调用clearerr(fp),使ferror(fp)的值变为0,以便再进行下一次的检测
只要出现文件读写错误标志,它就一直保留,直到对同一文件调用clearerr函数或rewind函数,或任何其他一个输入输出函数为止。
实例代码如下:
#include <stdio.h>
int main()
{
FILE *fp;
char c;
fp = fopen("file.txt", "r");
c = fgetc(fp);
if( ferror(fp) )
{
printf("读取文件:file.txt 时发生错误\n");
}
clearerr(fp);
if( ferror(fp) )
{
printf("读取文件:file.txt 时发生错误\n");
}
fclose(fp);
return(0);
}
输出函数时出现错误,ferror函数值为一个非零值。使用完ferror函数之后,应立即调用clearerr(fp),使ferror(fp)的值变为0,以便再进行下一次的检测
只要出现文件读写错误标志,它就一直保留,直到对同一文件调用clearerr函数或rewind函数,或任何其他一个输入输出函数为止。
实例代码如下:
#include <stdio.h>
int main()
{
FILE *fp;
char c;
fp = fopen("file.txt", "r");
c = fgetc(fp);
if( ferror(fp) )
{
printf("读取文件:file.txt 时发生错误\n");
}
clearerr(fp);
if( ferror(fp) )
{
printf("读取文件:file.txt 时发生错误\n");
}
fclose(fp);
return(0);
}