为什么使用文件
如果没有⽂件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运⾏程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们只能使用文件。
文件认识
程序文件
程序⽂件包括源程序⽂件(后缀为.c),⽬标⽂件(windows环境后缀为.obj),可执⾏程序(windows环境后缀为.exe)。
数据文件
⽂件的内容不⼀定是程序,而是程序运⾏时读写的数据,⽐如程序运⾏需要从中读取数据的⽂件,或者输出内容的⽂件。
在以前各章所处理数据的输入输出都是以终端为对象的,即从终端的键盘输⼊数据,运⾏结果显示到显示器上。
其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使⽤,这⾥处理的就是磁盘上⽂件。
文件名
⼀个文件要有⼀个唯⼀的文件标识,以便用户识别和引用。
⽂件名包含3部分:文件路径+⽂件名主⼲+文件后缀
例如: c:\code\test.txt
为了方便起见,文件标识常被称为文件名。
⼆进制文件和文本文件
根据数据的组织形式,数据⽂件被称为文本文件或者⼆进制文件。
数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存的文件中,就是⼆进制文件。
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的⽂件就是文本文件。
⼀个数据在文件中是怎么存储的呢?
字符⼀律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用⼆进制形式存储。
文件的打开和关闭
流和标准流
流
我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出操作各不相同,为了⽅便程序员对各种设备进⾏⽅便的操作,我们抽象出了流的概念。
C程序针对⽂件、画面、键盘等的数据输⼊输出操作都是通过流操作的。
标准流
那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢?那是因为C语⾔程序在启动的时候,默认打开了3个流:
• stdin标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。
• stdout标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出
流中。
• stderr标准错误流,⼤多数环境中输出到显⽰器界⾯。
这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。
文件指针
FILE* pf;//⽂件指针变量
文件操作符详解
文件的打开:
fopen():打开文件
文件的关闭:
fclose():关闭文件
文件的读写:
fgetc():读取一个字符
fputc():写入一个字符
fgets():读取一个字符串
fputs():写入一个字符串
fprintf():写入格式化数据
fscanf():格式化读取数据
fread():读取数据
fwrite():写入数据
文件状态检查
feof():文件是否结束
ferror():文件读/写是否出错
clearerr():清除文件错误标志
ftell():文件指针的当前位置
文件指针定位
rewind():把文件指针移到开始处
fseek():重定位文件指针
参数解释:
“r”:以只读的形式打开文本文件(不存在则出错)
“w”:以只写的形式打开文本文件(若不存在则新建,反之,则从文件起始位置写,覆盖原内容)
“a”:以追加的形式打开文本文件(若不存在,则新建;反之,在原文件后追加)
“r+”:以读写的形式打开文本文件(读时,从头开始;写时,新数据只覆盖所占的空间)
“wb”:以只写的形式打开二进制文件
“rb”:以只读的形式打开二进制文件
“ab”:以追加的形式打开一个二进制文件
“rb+”:以读写的形式打开二进制文件。
“w+”:首先建立一个新文件,进行写操作,然后从头开始读(若文件存在,原内容将全部消失)
“a+”:功能与”a”相同。只是在文件尾部追加数据后,可以从头开始读
“wb+”:功能与”w+”相同。只是在读写时,可以由位置函数设置读和写的起始位置
“ab+”:功能与”a+”相同。只是在文件尾部追加数据之后,可以由位置函数设置开始读的起始位置
文件操作符示例
文件在读写之前应该先打开文件,在使⽤结束之后应该关闭文件。
在编写程序的时候,在打开文件的同时,都会返回⼀个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。
//打开⽂件
FILE * fopen ( const char * filename, const char * mode );
//关闭⽂件
int fclose ( FILE * stream );
读写以字符串为例利用实例了解一下(顺序读写):
文件写入字符串代码:
/* fopen fclose example */
#include <stdio.h>
int main ()
{
FILE * pFile;
//打开⽂件
pFile = fopen ("myfile.txt","w");
//⽂件操作
if (pFile!=NULL)
{
fputs ("fopen example",pFile);//文件写入
//关闭⽂件
fclose (pFile);
}
return 0;
}
读取字符串代码:
int main()
{
//1. 打开文件
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//2. 读文件
char arr[20] = "xxxxxxxxxxxxx";
fgets(arr, 10, pf);
printf("%s\n", arr);
//3. 关闭文件
fclose(pf);
pf = NULL;
return 0;
}
读取数据块
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
ptr:目标内存块
size:一次读取的字节大小
count:一次读取多少个 size
stream:流
#include <stdio.h>
#include <string.h>
int main()
{
FILE *pFile = fopen("file.txt", "rb");
if (pFile == NULL)
{
perror ("Error opening file");
return 0;
}
char buf[100] = { 0 };
while (!feof(pFile)) //没有到文件末尾
{
memset(buf, 0, sizeof(buf));
size_t len = fread(buf, sizeof(char), sizeof(buf), pFile);
printf("buf: %s, len: %d\n", buf, len);
}
fclose(pFile);
}
写入数据块
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
与写入数据块同理不再做过多解释。
fseek():
int fseek ( FILE * stream, long int offset, int origin );
stream:流
offset:相对应 origin 位置处的偏移量,单位为字节
origin:指针的位置
/* fseek example */
#include <stdio.h>
int main ()
{
FILE * pFile;
pFile = fopen ( "example.txt" , "wb" );
fputs ( "This is an apple." , pFile );
fseek ( pFile , 9 , SEEK_SET );
fputs ( " sam" , pFile );
fclose ( pFile );
return 0;
}
获取指针位置
long int ftell ( FILE * stream );
stream:流
/* ftell example : getting size of a file */
#include <stdio.h>
int main ()
{
FILE * pFile;
long size;
pFile = fopen ("myfile.txt","rb");
if (pFile==NULL)
perror ("Error opening file");
else
{
fseek (pFile, 0, SEEK_END); // non-portable
size=ftell (pFile);
fclose (pFile);
printf ("Size of myfile.txt: %ld bytes.\n",size);
}
return 0;
}
文件指针移到开始处
void rewind( FILE *stream );
stream:流
清除文件错误标志
void clearerr( FILE *stream );
stream:流
文件流是否读到了文件尾
int feof( FILE *stream );
stream:流
重命名文件
int rename ( const char * oldname, const char * newname );
oldname:原名
newname:新名
删除文件
int remove ( const char * filename );
filename:文件的路径
--------------------------------分隔符
本次介绍结束了,因篇幅原因后面几个函数介绍略微潦草。
对文件的操作不常用,我们仅了解了解就可以。
有兴趣的可以自己再研究研究。