基于c语言的文件操作
对文件的操作分为两种方式:流式文件操作和I/O文件操作。
流式文件操作
这种方式的文件操作有一个重要的结构FILE,FILE在stdio.h中定义如下:
typedef struct
{
void* _Placeholder;
} FILE;
FILE这个结构包含了文件操作的基本属性,对文件的操作都要通过这个结构的指针来进行,此种文件操作常用的函数见下表
函数 | 功能 |
fopen() | 打开流 |
fclose() | 关闭流 |
fputc() | 写一个字符到流中 |
fgetc() | 从流中读一个字符 |
fseek() | 在流中定位到指定的字符 |
fputs() | 写字符串到流 |
fgets() | 从流中读一行或指定个字符 |
fprintf() | 按格式输出到流 |
fscanf() | 从流中按格式读取 |
feof() | 到达文件尾时返回真值 |
ferror() | 发生错误时返回其值 |
rewind() | 复位文件定位器到文件开始处 |
remove() | 删除文件 |
fread() | 从流中读指定个数的字符 |
fwrite() | 向流中写指定个数的字符 |
tmpfile() | 生成一个临时文件流 |
tmpnam() | 生成一个唯一的文件名 |
fopen
- 函数功能
打开一个文件 - 头文件
#include <stdio.h> - 函数原型:
FILE* fopen(
_In_z_ char const* _FileName,
_In_z_ char const* _Mode
);
- 参数说明
filename :欲打开的文件路径及文件名
mode : 代表流形式
参数 | 说明 |
r | 只读方式打开,文件必须存在 |
r+ | 以读/写方式打开文件,该文件必须存在 |
rb+ | 以读/写方式打开一个二进制文件,只允许读/写数据 |
rt+ | 以读/写方式打开一个文本文件,允许读和写 |
w | 打开只写文件,若文件存在则长度清为 0,即该文件内容消失,若不存在则创建该文件 |
w+ | 打开可读/写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件 |
a | 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留(EOF 符保留) |
a+ | 以附加方式打开可读/写的文件。若文件不存在,则会建立该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(原来的 EOF 符不保留)。 |
wb | 以只写方式打开或新建一个二进制文件,只允许写数据。 |
wb+ | 以读/写方式打开或建立一个二进制文件,允许读和写。 |
wt+ | 以读/写方式打开或建立一个文本文件,允许读写 |
at+ | 以读/写方式打开一个文本文件,允许读或在文本末追加数据。 |
ab+ | 以读/写方式打开一个二进制文件,允许读或在文件末追加数据。 |
以 x 结尾的模式为独占模式,文件已存在或者无法创建(一般是路径不正确)都会导致 fopen 失败。文件以操作系统支持的独占模式打开。
上述的形态字符串都可以再加一个 b 字符,如 rb、w+b 或 ab+ 等组合,加入 b 字符用来告诉函数库以二进制模式打开文件。如果不加 b,表示默认加了 t,即 rt、wt,其中 t 表示以文本模式打开文件。由 fopen() 所建立的新文件会具有 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH(0666) 权限,此文件权限也会参考umask值。
- 返回值
文件顺利打开后,指向该流的文件指针就会被返回。
文件打开失败则返回 NULL,并把错误代码存在 error 中。
一般而言,打开文件后会做一些文件读取或写入的动作,若打开文件失败,接下来的读写动作也无法顺利进行,所以一般在 fopen() 后作错误判断及处理。
- 程序演示:
#include <stdio.h>
#define F_PATH "d:\\myfile\\file.dat"
int main(void)
{
FILE *fp = NULL; /* 需要注意 */
fp = fopen(F_PATH, "r");
if (NULL == fp)
{
return -1; /* 要返回错误代码 */
}
fclose(fp);
fp = NULL; /* 需要指向空,否则会指向原打开文件地址 */
return 0;
}
#include <stdio.h>
#include <stdlib.h> /* 为了使用exit() */
int main(void)
{
int i = 0; /* 用于 putchar & getc 的数据接收 */
char *ch = "";
FILE *fp = NULL;
char fname[50]; /* 用于存放文件名 */
printf("输入文件名:");
scanf("%s", fname);
fp = fopen(fname, "r"); /* 只供读取 */
if (NULL == fp) /* 如果失败了 */
{
printf("错误!");
exit(1); /* 中止程序 */
}
while ((ch[i] = getc(fp)) != EOF)
{
putchar(ch[i]);
i ++;
}
fclose(fp); /* 关闭文件 */
fp = NULL; /* 需要指向空,否则会指向原打开文件地址 */
return 0;
}
注意!初学者往往会犯一个错误,即在输入文件名时不加后缀名,请注意加上!
更多: https://baike.baidu.com/item/fopen/10942321?fr=aladdin
fwrite:实现对数据块的写
- 功能:
将一块内存区域中的数据写入到本地文本。 - 函数原型
unsigned int fwrite(
void const* _Buffer, //指向数据块的指针
unsigned int _ElementSize, //每个数据的大小,单位为Byte(例如:sizeof(int)就是4)
unsigned int _ElementCount, //数据个数
FILE* _Stream //文件指针
);
- 返回值
返回值随着调用格式的不同而不同:
(1) 调用格式:fwrite(buf,sizeof(buf),1,fp);
成功写入返回值为1(即count)
(2)调用格式:fwrite(buf,1,sizeof(buf),fp);
成功写入则返回实际写入的数据个数(单位为Byte) - 举例
将1024个(int)字节写入文本文件
#define DATA_SIZE 1024
int main(){
unsigned int *dataPtr=NULL;
dataPtr=(unsigned int *)malloc(sizeof(int)*DATA_SIZE);
for(unsigned int i=0;i<DATA_SIZE;i++){
dataPtr[i]=i;
}
FILE *fp=fopen("D:\\1.txt","w");
fwrite(dataPtr,sizeof(int),DATA_SIZE,fp);
fwrite(dataPtr,1,sizeof(unsigned int)*DATA_SIZE,fp);
fcolse(fp);
free(dataPtr);
return 0;
}
下面的代码将4096个char数据写到文本,写入的数据中,最大值为255,与上面代码有区别,因为缓存区数据类型不同
#define DATA_SIZE 1024
int main(){
unsigned char *dataPtr=NULL;
//申请的数据缓冲区是4096个char
dataPtr= (unsigned char*)malloc(sizeof(int)*DATA_SIZE);
for(unsigned int i=0;i<DATA_SIZE;i++){
dataPtr[i]=i;
}
FILE *fp=fopen("D:\\1.txt","ad+");
fwrite(dataPtr,sizeof(char),DATA_SIZE*sizeof(int),fp);
fclose(fp);
free(dataPtr);
return 0;
}
用malloc函数申请区域时是申请的一片char*区域,通过强制类型转换后可装unsigned int 数据。
#define DATA_SIZE 1024
int main(){
unsigned char *dataPtr=NULL;
unsigned int *p=NULL;
//申请的数据缓冲区是4096个char
dataPtr= (unsigned char*)malloc(sizeof(int)*DATA_SIZE);
//将char型缓存区强转成int型缓冲区
p=dataPtr;
for(unsigned int i=0;i<DATA_SIZE;i++){
dataPtr[i]=i;
}
FILE *fp=fopen("D:\\1.txt","ad+");
fwrite(dataPtr,sizeof(unsigned int ),DATA_SIZE,fp);
fclose(fp);
free(dataPtr);
return 0;
}
fread: 实现对数据块的读
- 功能
从一个文件流中读取数据 - 函数原型
unsigned int fread(
void* _Buffer, //指向数据块的缓冲区
unsigned int _ElementSize, //每个数据的大小,单位为Byte(例如:sizeof(int)就是4)
unsigned int _ElementCount, //数据的个数
FILE* _Stream //文件指针
);
- 返回值
返回值随着调用格式的不同而不同:
- 调用格式:fread(buf,sizeof(buf),1,fp);
读取成功时:当读取的数据量正好是sizeof(buf)个Byte时,返回值为1(即count)
否则返回值为0(读取数据量小于sizeof(buf)) - 调用格式:fread(buf,1,sizeof(buf),fp);
读取成功返回值为实际读回的数据个数(单位为Byte)
例如:fread(fa,4,5,fp); 其意义是从fp所指的文件中,每次读4个字节(一个实数)送入实数组fa中,连续读5次,即读5个实数到fa中
int main(){
FILE *fp=NULL;
char fileDir[] ="D:\\1.txt";
char data="Hello world";
printf("sizeof(data) =%1d\n",sizeof(dataPtr));
file=fopen(fileDir,"W+");
//返回值为1
int writecount=fwrite(data,sizeof(data),1,fp);
printf("writecount= %d\n",writecount);
fclose(fp);
fp=NULL;
fp=fopen(fileDir,"r");
char buffer[255];
//当读取的数据量正好是sizeof(buf)个Byte时,返回值为1(即count) 否则返回值为0(读取数据量小于sizeof(buf))
int readcount=fread(buffer,sizeof(buffer),1,fp); //返回0
int readcount1=fread(buffer,1,sizeof(buffer),fp);//返回读取的实际个数
fclose(fp);
printf("%s\n",buffer);
return 0;
}
fgetc:字符的读取
- 调用方式
int fgetc(
FILE* _Stream //读取的文件
);
ch=fgetc(fp)
- 功能
从指定的文件读入一个字符,即从fp所指向的文件中读入一个字符赋给ch。 - 返回值
成功:返回所读的字符
失败:返回EOF(-1) - 举例
void main()
{
FILE *fp;
int c;
fp=fopen("exist","r");
while((c=fgetc(fp))!=EOF)
printf("%c",c);
fclose(fp);
}
fputc:字符的写入
- 调用方式
fputc(ch,fp);
int fputc(
int _Character, //写入的字符
FILE* _Stream //写入的文件指针
);
fputc(ch,fp);
- 功能
将一个字符写入到磁盘文件上,即将字符ch输入到指针所指定的文件中 - 返回值
成功: 函数返回写入文件的字符的ASCII码值
失败:返回EOF(-1)
当正确写入一个字符或一个字节的数据后,文件内部写指针会自动后移一个字节的位置。EOF是在头文件 stdio.h中定义的宏。
- 举例
#include <stdio.h>
#include <stdlib.h>
void main(){
FILE*fpout;
char ch;
if((fpout=fopen("file_a.dat","w"))==NULL){
printf("Error!\n");
exit;
}
ch=getchar();
for(;ch!='#';){
fputc(ch,fpout);
ch=getchar(); //不能仅写getchar();
}
fclose(fpout);
}
fgets:读字符串函数
- 功能
从文件结构体指针stream中读取数据,每次读取一行。读取的数据保存在buf指向的字符数组中,每次最多读取maxCount-1个字符(第maxCount个字符赋’\0’),如果文件中的该行,不足maxCount-1个字符,则读完该行就结束。如若该行(包括最后一个换行符)的字符数超过maxCount-1,则fgets只返回一个不完整的行,但是,缓冲区总是以NULL字符结尾,对fgets的下一次调用会继续读该行。 - 函数声明:
char* fgets(
char* _Buffer, //字符型指针,指向用来存储所得数据的地址。
int _MaxCount, //整型数据,指明存储数据的大小。表示从文件中读出的字符串不超过 MaxCount-1个字符。在读入的最后一个字符后加上串结束标志'\0'
FILE* _Stream //文件结构体指针,将要读取的文件流。
);
- 返回值
- 成功,则返回第一个参数buf;
- 在读字符时遇到end-of-file,则eof指示器被设置,如果还没读入任何字符就遇到这种情况,则buf保持原来的内容,返回NULL;
- 如果发生读入错误,error指示器被设置,返回NULL,buf的值可能被改变。
因此我们不能直接通过fgets的返回值来判断函数是否是出错而终止的,应该借助feof函数或者ferror函数来判断。
- 举例
int main ( void ){
FILE*stream;
char string[]="Thisisatest";
char msg[20];
stream=fopen("D:\\1.txt","w+"); //打开文件
fwrite(string,strlen(string),1,stream); //将字符串写入文件
fseek(stream,0,SEEK_SET); //定位到文件开头
fgets(msg,strlen(string)+1,stream); //获取文件中的一行字符串,赋给字符数组,并在字符串末尾加'\0'
printf("%s",msg);
fclose(stream);
return 0;
}
- 函数使用
同时可以用作键盘输入:fgets(key,n,stdin)且还必须:key[strlen(key)]=’\0’或者key[n-1]=’\0’
还有种程序经常使用的方法:key[strlen(key-1)]=0x00;
与gets相比使用这个好处是:读取指定大小的数据,避免gets函数从stdin接收字符串而不检查它所复制的缓存的容积导致的缓存溢出问题。
fputs:写入字符串
- 功能
向指定的文件写入一个字符串(不自动写入字符串结束标记符‘\0’)。 - 函数声明
int fputs(
char const* _Buffer, //这是一个数组,包含了要写入的以空字符终止的字符序列。
FILE* _Stream //指向 FILE 对象的指针,该 FILE 对象标识了要被写入字符串的流
);
- 返回值
- 成功写入一个字符串后,文件的位置指针会自动后移,函数返回值为非负整数;
- 否则返回EOF(符号常量,其值为-1)。
- 举例
int main(){
char str[80]="my name is yj\n";
FILE *fp = NULL;
if((fp=fopen("D:\\1.txt","w"))==NULL){
printf("cannot open file\n");
exit(0);
}
fputs(str,fp);
//putchar(str);
fclose(fp);
fp = NULL;
return 0;
}
fscanf
- 功能
根据数据格式(format)从输入流(stream)中写入数据(argument);fscanf遇到空格和换行时结束,注意空格时也结束。这与fgets有区别,fgets遇到空格不结束。 - 函数声明
int fscanf(
FILE* const _Stream, //文件指针
char const* const _Format, //格式字符串
[argument...] //输入列表
)
例子:FILE *fp;
char a[];
int b;
double c;
fscanf(fp,"%s%d%lf",a,&b,&c)
- 返回值
- 成功:返回读入的参数的个数,数值等于[argument…]的个数
- 失败:返回EOF(-1)
- 格式字符说明
常用基本参数 | 作用 |
%d | 读入一个十进制整数. |
%i | 读入十进制,八进制,十六进制整数,与%d类似,但是在编译时通过数据前置或后置来区分进制,如加入“0x”则是十六进制,加入“0”则为八进制。例如串“031”使用%d时会被算作31,但是使用%i时会算作25. |
%u | 读入一个无符号十进制整数. |
%f %F %g %G | 用来输入实数,可以用小数形式或指数形式输入. |
%x %X | 读入十六进制整数. |
%o | 读入八进制整数. |
%s | 读入一个字符串,遇空字符‘\0’结束。 |
%c | 读入一个字符。无法读入空值。空格可以被读入。 |
L/l | 长度修饰符 输入"长"数据 |
h | 长度修饰符 输入"短"数据 |
fprintf
- 功能
会根据参数format 字符串来转换并格式化数据, 然后将结果输出到参数stream 指定的文件中, 直到出现字符串结束(’\0’)为止。 - 函数声明
int fprintf(
FILE* const _Stream, //文件指针
char const* const _Format, //格式字符串
[argument...] //输出列表
)
例如: fprintf(fp, “%s %s %d %f”, str1,str2, a, b) ;
- 举例
int main(void){
FILE *stream;
long l;
float fp;
char s[81];
char c;
stream = fopen("fscanf.out", "w+");
if(stream==NULL)
printf("The file fscanf.out was not opened\n");
else
{
fprintf(stream,"%s%ld%f%c","a-string", 65000,3.14159, 'x');
/*将指针设置至文件开头*/
fseek(stream,0L,SEEK_SET);
/*从文件中读取数据*/
fscanf(stream,"%s",s);
fscanf(stream,"%ld",&l);
fscanf(stream,"%f",&fp);
fscanf(stream,"%c",&c);
/*输出读取的数据*/
printf("%s\n",s);
printf("%ld\n",l);
printf("%f\n",fp);
printf("%c\n",c);
fclose(stream);
}
return 0;
}//这样会有意外输出
ftell
- 功能
得到流式文件当前读写位置 - 声明
long ftell(
FILE* _Stream
);
- 返回值
其返回值是当前读写位置偏离文件头部的字节数。
fseek
- 功能
把文件读写位置指针移到指定的位置。 - 函数声明
int fseek(
FILE* _Stream, //文件指针
long _Offset, //漂移的个数,正数表示正向偏移,负数表示负向偏移
int _Origin /*代表起始点 它有三个常量的值,如下:
起始点 对应的数字 代表的文件位置
SEEK_SET 0 文件开头
SEEK_CUR 1 文件当前位置
SEEK_END 2 文件末尾
*/
);
- 返回值
如果执行成功,stream将指向以_origin为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。
如果执行失败(比如offset取值大于等于210241024*1024,即long的正数范围2G),则不改变stream指向的位置,函数返回一个非0值。 - 举例
fseek(fp,100L,0); 把stream指针移动到离文件开头100字节处;
fseek(fp,100L,1); 把stream指针移动到离文件当前位置100字节处;
fseek(fp,-100L,2); 把stream指针退回到离文件结尾100字节处。
long filesize(FILE*stream){
long curpos,length;
curpos=ftell(stream); //获取当前读写位置偏离文件头部的字节数。
fseek(stream,0L,SEEK_END); //重新定位到文件的末尾位置
length=ftell(stream); //获取当前读写位置(当前指针在末尾)偏离文件头部的字节数,即文件总长度
fseek(stream,curpos,SEEK_SET); //从文件头部正向偏离curpos个位置,即回到原来的位置
return length; //获取文件长度
}
rewind
- 功能
将文件指针重新指向一个流的开头 - 声明
void rewind(
FILE* _Stream
);
- 举例
一般我们会通过下面的方法来获取文件中字符的个数:
FILE *fs=fopen("D:\1.txt","r");//创建文件流
long length=0; //声明文件长度
fseek(fs,0,SEEK_END); //将文件内部指针放到文件最后面
length=ftell(fs); //读取文件指针的位置,得到文件字符的个数
rewind(fs); //将文件指针重置到文件最前面
ferror
- 功能
用来检查参数stream 所指定的文件流是否发生了错误情况, 如有错误发生,则返回非0 值. - 函数声明
int ferror(
FILE* _Stream
);
feof
- 功能
feof是C语言标准库函数,其原型在stdio.h中,其功能是检测流上的文件结束符,如果文件结束,则返回非0值,否则返回0(即,文件结束:返回非0值,文件未结束,返回0值),文件结束符只能被clearerr()清除。(这里的检测流上的文件结束符就相当于声卡检测电流信号的一个过程) - 形式
int feof(FILE *stream); - 参数
流: FILE 结构的指针
注意:feof判断文件结束是通过读取函数fread/fscanf等返回错误来识别的,故而判断文件是否结束应该是在读取函数之后进行判断。比如,在while循环读取一个文件时,如果是在读取函数之前进行判断,则如果文件最后一行是空白行,可能会造成内存错误。
- 举例
#include<stdio.h>
int main(){
FILE *fp;
fp=fopen("1.txt","r");
fgetc(fp);
if(feof(fp)){ //判断文件是否结束
printf("succeed reading");
}
fclose(fp);
return 0;
}
- 深入了解
EOF是文本文件结束的标志。在文本文件中,数据是以字符的ASCⅡ代码值的形式存放,普通字符的ASCⅡ代码的范围是32到127(十进制),EOF的16进制代码为0xFF(十进制为-1),因此可以用EOF作为文件结束标志。
当把数据以二进制形式存放到文件中时,就会有-1值的出 现,因此不能采用EOF作为二进制文件的结束标志。为解决这一个问题,ASCI C提供一个feof函数,用来判断文件是否结束。feof函数既可用以判断二进制文件又可用以判断文本文件。