1.文件的概念
文件的基本概念
所谓“文件”是指一组相关数据的有序集合。 这个数据集有一个名称,叫做文件名。实际上在前面的各章中我们已经多次使用了文件,例如源程序文件、目标文件、可执行文件、库文件 (头文件)等。文件通常是驻留在外部介质(如磁盘等)上的,在使用时才调入内存中来。从不同的角度可对文件作不同的分类。从用户的角度看,文件可分为普通文件和设备文件两种。
普通文件是指驻留在磁盘或其它外部介质上的一个有序数据集,可以是源文件、目标文件、可执行程序; 也可以是一组待输入处理的原始数据,或者是一组输出的结果。对于源文件、目标文件、 可执行程序可以称作程序文件,对输入输出数据可称作数据文件。
设备文件是指与主机相联的各种外部设备,如显示器、打印机、键盘等。在操作系统中,把外部设备也看作是一个文件来进行管理,把它们的输入、输出等同于对磁盘文件的读和写。 通常把显示器定义为标准输出文件,一般情况下在屏幕上显示有关信息就是向标准输出文件输出。如前面经常使用的printf,putchar 函数就是这类输出。键盘通常被指定标准的输入文件, 从键盘上输入就意味着从标准输入文件上输入数据。scanf,getchar函数就属于这类输入。
从文件编码的方式来看,文件可分为ASCII码文件和二进制码文件两种。
ASCII文件也称为文本文件,这种文件在磁盘中存放时每个字符对应一个字节,用于存放对应的ASCII码。例如,数5678的存储形式为:
ASC码: 00110101 00110110 00110111 00111000
↓ ↓ ↓ ↓
十进制码: 5 6 7 8 共占用4个字节。ASCII码文件可在屏幕上按字符显示, 例如源程序文件就是ASCII文件,用DOS命令TYPE可显示文件的内容。 由于是按字符显示,因此能读懂文件内容。
二进制文件是按二进制的编码方式来存放文件的。 例如, 数5678的存储形式为: 00010110 00101110只占二个字节。二进制文件虽然也可在屏幕上显示,但其内容无法读懂。C系统在处理这些文件时,并不区分类型,都看成是字符流,按字节进行处理。输入输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制。 因此也把这种文件称作“流式文件”。
本章讨论流式文件的打开、关闭、读、写、定位等各种操作。文件指针在C语言中用一个指针变量指向一个文件, 这个指针称为文件指针。通过文件指针就可对它所指的文件进行各种操作。定义说明文件指针的一般形式为: FILE* 指针变量标识符; 其中FILE应为大写,它实际上是由系统定义的一个结构,该结构中含有文件名、文件状态和文件当前位置等信息。 在编写源程序时不必关心FILE结构的细节。例如:FILE *fp;表示fp是指向FILE结构的指针变量,通过fp 即可找存放某个文件信息的结构变量,然后按结构变量提供的信息找到该文件,实施对文件的操作。习惯上也笼统地把fp称为指向一个文件的指针。文件的打开与关闭文件在进行读写操作之前要先打开,使用完毕要关闭。所谓打开文件,实际上是建立文件的各种有关信息,并使文件指针指向该文件,以便进行其它操作。关闭文件则断开指针与文件之间的联系,也就禁止再对该文件进行操作。
2.文件操作函数的介绍
1.fopen
函数原型:
函数说明:
filenmae为文件名,mode是打开方式,如果以“r"的方式打开,并且此文件不存在则会出错,下面我们来看一个例子:
#include<stdio.h>
int main()
{
FILE*fp=fopen("text.txt", "r");
if (fp == NULL)
{
perror("fopen:");
return -1;
}
fclose(fp);
return 0;
}
此时我们用fopen这个函数来打开text.txt这个文件,并且是以"r"的形式来打开的。而我并没有创建这个文件,所以此次打开是失败的,我们可以用perror来打印错误信息
运行结果:
如果我们以读的方式打开,如果打开的文件不存在它会自动生成。如果打开的文件存在,则会将文件里面的内容清空
下面我们来看例子:
#include<stdio.h>
int main()
{
FILE*fp=fopen("text.txt", "w");
if (fp == NULL)
{
perror("fopen:");
return -1;
}
fclose(fp);
return 0;
}
我们可以发现当运行程序的时候,他会自动生成对应的文件
如果我此时点进去,并输入数据进去,并将其保存
当运行完这个程序之后:
我们可以发现里面的数据被清空了
总结:打开文件的作用:
1)分配给打开文件一个FILE 类型的文件结构体变量,并将有关信息填入文件结构体变量;
(2)开辟一个缓冲区;
(3)调用操作系统提供的打开文件或建立新文件功能,打开或建立指定文件;
FILE *:指出fopen是一个返回文件类型的指针函数;返回值
正常返回:被打开文件的文件指针。
异常返回:NULL,表示打开操作不成功。
要说明的是:C语言将计算机的输入输出设备都看作是文件。例如,键盘文件、屏幕文件等。ANSI C标准规定,在执行程序时系统先自动打开键盘、屏幕、错误三个文件。这三个文件的文件指针分别是:标准输入stdin、标准输出stdout和标准出错 stderr。
fclose():文件关闭
3.fclose:
1.函数原型:
函数说明:stream
返回值:0表示正常返回
异常返回:EOF,表示文件关闭失败;或者关闭时发生错误
fgetc()读取一个字符
1.函数原型:
函数说明:
从fp中读取一个字符,并作为返回值返回
返回值:
1.正常返回:
返回读取字符的ASSCLL值
2.异常返回:
读取失败,则会返回EOF.例如:如果我们以写的形式打开,但是我们使用fgetc()去读取一个字符时,则会发生错误而返回EOF
例:
#include<stdio.h>
int main()
{
FILE*fp=fopen("text.txt", "r");
if (fp == NULL)
{
perror("fopen:");
return -1;
}
int ch = fgetc(fp);
printf("%d", ch);
fclose(fp);
fp = NULL;
return 0;
}
运行结果:
注意:之所以会打印-1,这是因为EOF是一个宏,值就是-1;
正常用法:
在这里我先往text.txt中输入数据
然后在读取:
#include<stdio.h>
int main()
{
FILE*fp=fopen("text.txt", "r");
if (fp == NULL)
{
perror("fopen:");
return -1;
}
int ch=fgetc(fp);
printf("%c", ch);
fclose(fp);
fp = NULL;
return 0;
}
运行结果:
重点!!!!fgets()从所有输入流中读取字符串
1.函数原型:
函数说明:
函数说明:由fp指出的文件中读取n-1个字符,并把他们存放到有str指出的字符数组中区,最后加上一个由字符串结束符'\0'
参数说明:str:接受字符串的内存地址,可以是数组别名,也可以是指针
n:指出要读取的字符的个数
fp:这个是文件指针,指出要从中读取字符的文件
返回值:
正常返回:字符串的内存首地址,即str的值
异常返回:返回一个NULL值,此时应当用feof()或ferror()函数来判别是读取到了文件尾,
2.标准库中的实现:
/****************************************************
char *fgets(char *s, int n, FILE *stream)
{
register int c;
register char *cs;
cs=s;
while(--n>0 &&(c = getc(stream))!=EOF)
if ((*cs++= c) =='\n')
break;
*cs ='\0';
return (c == EOF && cs == s) ?NULL :s ;
}
/********************************************************
使用注意:
在用fgets(..)读入数据时,先定义一个字符数组或字符指针,如果定义了字符指针 ,那么一定要初始化。
char*s;//不可以
char arr[20];//可以
所以如果要使用指针我们可以使用动态内存开辟的方式为其分配空间
char *s=(char*)malloc(sizeof(char)*20);
3. fgets()读取文本行时有两种情况:
1.当要读取的个数大于一行字符串的长度,那么当读到末尾\n时,fegets将\n取走之后就停止读取了,并且在字符串s的末尾加上\0
首先在对应文件中输入数据。在运行下列程序
代码:
#include<stdio.h>
int main()
{
FILE*fp=fopen("text.txt", "r");
if (fp == NULL)
{
perror("fopen:");
return -1;
}
char tmp[20];
fgets(tmp, 12, fp);
printf(tmp);
fclose(fp);
fp = NULL;
return 0;
}
我们可以通过调试来看是不是这样的:
我们可以发现末尾的\n被读走
2.当要读取的个数小于一行的个数时,此时只会读取n-1个字符
同样是上面的代码我们只改变读取个数:
#include<stdio.h>
int main()
{
FILE*fp=fopen("text.txt", "r");
if (fp == NULL)
{
perror("fopen:");
return -1;
}
char tmp[20];
fgets(tmp, 4, fp);
printf(tmp);
fclose(fp);
fp = NULL;
return 0;
}
同样的我们通过调试的方法进行验证:
我们可以发现,我们是想要读取4个字符但是他只读取了3个字符,也就是说当我们想要读取n个字符时,它实际上只会读取n-1个字符
4.fgets和gets
在一定的情况下可以使用fgets代替gets
fgets(...)从标准设备读数据。
用fgets(...)还也读入标准输入设备(一般为键盘)的信息
原型 : fgets(s,n,stdin);
假设在控制台下,我们可以用fgets(...)替代gets(),读入键盘输入的信息,fgets()是安全的,因为不会像gets()有溢出的可能。。
比如 :输入 abc
fgets(s,n,stdin)也会读入n-1个字符。但是只是从stdin流读入
fgets函数fgets函数用来从文件中读入字符串。fgets函数的调用形式如下:fgets(str,n,fp);此处,fp是文件指针;str是存放在字符串的起始地址;n是一个int类型变量。函数的功能是从fp所指文件中读入n-1个字符放入str为起始地址的空间内;如果在未读满n-1个字符之时,已读到一个换行符或一个EOF(文件结束标志),则结束本次读操作,读入的字符串中最后包含读到的换行符。因此,确切地说,调用fgets函数时,最多只能读入n-1个字符。读入结束后,系统将自动在最后加'\0',并以str作为函数值返回。
gets()将删除新行符, fgets()则保留新行符.
要去掉fgets()最后带的“\0",只要用 s[strlen(s)-1]='\0';即可。
fgets不会像gets那样自动地去掉结尾的\n,所以程序中手动将\n位置处的值变为\0,代表输入的结束。
针对于fgets,还要再说两句,下面这种用法,是安全的判断文件读取结束或者出错的好方式,切忌不能使用while(!feof(fp)) ,还有对于fgets的第二个参数是最大能读取文件字符的个数,一般最大的长度是1024字节。
while(fgets(..., stream)){
/* ... */
}
if(ferror(stream)){
/* ... */
}
fputs
1.函数原型:
函数说明;是将字符串string,输出到输出到文件或者屏幕上
返回值:
正常返回:
写入到文件中的个数
异常返回:
返回一个NULL,此时应该用feof()或者ferror函数来判断,是否读到文件的末尾,还是发生了错误
#include<stdio.h>
int main()
{
FILE*fp=fopen("text.txt", "w");
if (fp == NULL)
{
perror("fopen:");
return -1;
}
fputs("hello world", fp);
fclose(fp);
fp = NULL;
return 0;
}
运行之后:
我么可以发现对应的文件中:
fprintf
1.函数原型
2。函数说明:
2.功能说明
将变量表列(arg_list)中的数据,按照format指出的格式,写入由fp指定的文件。fprintf()函数与printf()函数的功能相同,只是printf()函数是将数据写入屏幕文件(stdout)。
3.参数说明
fp:这是个文件指针,指出要将数据写入的文件。
format:这是个指向字符串的字符指针,字符串中含有要写出数据的格式,所以该字符串成为格式串。格式串描述的规则与printf()函数中的格式串相同。
arg_list:是要写入文件的变量表列,各变量之间用逗号分隔。
例子:
#include<stdio.h>
int main()
{
FILE*fp=fopen("text.txt", "w");
if (fp == NULL)
{
perror("fopen:");
return -1;
}
int a = 10;
char tmp[] = "abceded";
fprintf(fp, "%d%s", a, tmp);
fclose(fp);
fp = NULL;
return 0;
}
运行结果:
同样他也能够像printf一样输出到屏幕上
#include<stdio.h>
int main()
{
FILE*fp=fopen("text.txt", "w");
if (fp == NULL)
{
perror("fopen:");
return -1;
}
int a = 10;
char tmp[] = "abceded";
fprintf(stdout, "%d %s", a, tmp);
fclose(fp);
fp = NULL;
return 0;
}
运行结果:
fread fwrite
1.函数原型:
2.函数说明:
2. 功能说明
从由fp指定的文件中,按二进制形式将sife*count个数据读到由buffer指出的数据区中。
3. 参数说明
buffer:这是一个void型指针,指出要将读入数据存放在其中的存储区首地址。
sife:指出一个数据块的字节数,即一个数据块的大小尺寸。
count:指出一次读入多少个数据块(sife)。
fp:这是个文件指针,指出要从其中读出数据的文件。
4.返回值
正常返回:实际读取数据块的个数,即count。
异常返回:如果文件中剩下的数据块个数少于参数中count指出的个数,或者发生了错误,返回0值。此时可以用feof()和ferror()来判定到底出现了什么
情况。
H. 以二进制形式写数据到文件中去
1. 函数原型
int fwrite(void *buffer,unsigned sife,unsigned count,FILE *fp)
2. 功能说明
按二进制形式,将由buffer指定的数据缓冲区内的sife*count个数据写入由fp指定的文件中去。
3. 参数说明
buffer:这是一个void型指针,指出要将其中数据输出到文件的缓冲区首地址。
sife:指出一个数据块的字节数,即一个数据块的大小尺寸。
count:一次输出多少个数据块(sife)。
fp:这是个文件指针,指出要从其中读出数据的文件。
4.返回值
正常返回:实际输出数据块的个数,即count。
异常返回:返回0值,表示输出结束或发生了错误。
例子:
#include<stdio.h>
int main()
{
FILE*fp=fopen("text1.txt", "wb");
if (fp == NULL)
{
perror("fopen:");
return -1;
}
int a = 10;
char tmp[] = "1abceded";
fwrite(tmp, sizeof(tmp), 1, fp);
fclose(fp);
fp = NULL;
return 0;
}
程序运行起来之后打开对应的文件我们可以发现
我们看不懂此时我们可以使用fread来读取
#include<stdio.h>
int main()
{
FILE*fp=fopen("text1.txt", "rb");
if (fp == NULL)
{
perror("fopen:");
return -1;
}
char arr[10]={0};
fread(arr,sizeof(char)*9,1,fp);
printf(arr);
fclose(fp);
fp = NULL;
return 0;
}
运行结果:
fseek()
1.函数原型:
2.函数说明:
2) 功能说明
使文件指针fp移到基于base的相对位置offset处。
(3)参数说明
fp:文件指针。
offset:相对base的字节位移量。这是个长整数,用以支持大于64KB的文件。
base:文件位置指针移动的基准位置,是计算文件位置指针位移的基点。ANSI C定义了base的可能取值,以及这些取值的符号常量。
(4)返回值
正常返回:当前指针位置。
异常返回:-1,表示定位操作出错。
例子:
#include <stdio.h>
#include <string.h>
struct std_type
{
int num;
char name[20];
int age;
char class;
}stud;
int cstufile()
{
int i;
FILE *fp;
if((fp=fopen("stufile","wb"))==NULL)
{
printf("The file can't be opened for write.\n");
return 0;
}
for(i=1;i<=100;i++)
{
stud.num=i;
strcpy(stud.name,"aaaa");
stud.age=17;
stud.class='8';
fwrite(&stud,sizeof(struct std_type),1,fp);
}
fclose(fp);
return 1;
}
void main()
{
int n;
FILE *fp;
if(cstufile()==0) return;
if((fp=fopen("stufile","rb"))==NULL)
{
printf("The file can not be opened.\n");
return;
}
for(n=0;n<100;n+=2)
{
fseek(fp,n*sizeof(struct std_type),SEEK_SET);
fread(&stud,sizeof(struct std_type),1,fp);
printf("%10d%20s%10d%4c\n",stud.num,stud.name,stud.age,stud.class);
}
fclose(fp);
}
补充:
关于exit()函数
1. 函数原型
void exit(int status)
2. 功能说明
exit()函数使程序立即终止执行,同时将缓冲区中剩余的数据输出并关闭所有已经打开的文件。
3. 参数说明
status:为0值表示程序正常终止,为非0值表示一个定义错误。
4. 返回值
无。
关于feof()函数
1. 函数原型
int feof(FILE *fp)
2. 功能说明
在文本文件(ASCII文件)中可以用值为-1的符号常量EOF来作为文件的结束符。但是在二进制文件中-1往往可能是一个有意义的数据,因此不能用它 来作为文件的结束标志。为了能有效判别文件是否结束,ANSI C提供了标准函数feof(),用来识别文件是否结束。
3. 参数说明
fp:文件指针。
4. 返回值
返回为非0值:已到文件尾。
返回为0值:表示还未到文件尾。
fscanf
1.函数原型:fscanf(FILE *fp, const char *format, agars)
2,函数说明:
1.fscanf():
fscanf()函数的头文件是<stdio.h>,
函数原型 为 int fscanf(FILE*stream, constchar*format, [argument...]);
其功能为根据数据格式(format)从输入流(stream)中写入数据(argument);
【参数】stream为文件指针,format为格式化字符串,argument 为格式化控制符对应的参数。
从文件指针fp指向的文件中,按format中对应的控制格式读取数据,并存储在agars对应的变量中;
#include<stdio.h>
int main()
{
FILE*fp=fopen("text.txt", "r");
if (fp == NULL)
{
perror("fopen:");
return -1;
}
int a;
double b;
fscanf(fp, "%d %lf", &a, &b);
printf("%d %lf", a, b);
fclose(fp);
fp = NULL;
return 0;
}
文件:
运行结果:
ssprintf()函数和sscanf()函数的用法
1、ssprinf( )函数的用法
sprintf的作用是将一个格式化的字符串输出到一个目的字符串中,而printf是将一个格式化的字符串输出到屏幕,其原型为:
int sprintf(char *str, char * format [, argument, ...]);
sprintf()会根据参数format 字符串来转换并格式化数据,然后将结果复制到参数str 所指的字符串数组,直到出现字符串结束('\0')为止。
【返回值】成功则返回参数str 字符串长度,失败则返回-1,
sprintf()最常见的应用之一莫过于把整数打印到字符串中,如:
sprintf(s, "%d", 123); //把整数123打印成一个字符串保存在s中
sprintf(s, "%8x", 4567); //小写16进制,宽度占8个位置,右对齐
2、sscanf( )函数的用法
sscanf与scanf类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源。
sscanf与scanf类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源。
函数原型:
int sscanf( string str, char *format, mixed var1, mixed var2 ... );
eg: sscanf(surBuf, "%[1-9]", dstBuf);
先说下%[]:其基本格式为 %[set],表示将接收一个由set指定格式的字符串,其中set表示可接受的字符集合。
set一般有两种情况:
一种是"^set"表示非, 即在输入的字符串中将匹配所有不在set中出现的字符,遇到set中的字符时停止匹配。
另一种是"set"表示在输入的字符串中将匹配所有在set中出现的字符,遇到非set中的字符时停止匹配。
对比一组函数
函数名称 | 函数功能 | ||
fscanf | 从所有输入流中读取格式化数据 | ||
scanf | 从标准输入流中读取格式化数据 | ||
sscnaf | 从字符串中读取格式化数据 |
函数名称 | 函数功能 |
printf | 把格式化数据输出到屏幕上去 |
fprintf | 把格式化数据输出到所有输出流中 |
sprintf | 把格式化数据转成字符串 |
请大佬多多指教!