前言
- 对计算机而言,一切皆数据,C语言源代码文件是数据、编译后的可执行程序也是数据,数据的存放方式有很多种,如内存、文件、数据库等,文件是极其重要的一种
- 根据文件中数据组织形式的不同,可以把文件分为文本文件和二进制文件,C语言源代码是文本文件,编译后的可执行程序是二进制文件
一、文本数据和二进制数据
文本数据
文本数据由字符串组成,存放了每个字符的 ASCII 码值,每个字符占一个字节,每个字节存放一个字符
比如数字 123,如果用文本格式存放,数据内容是'1'、'2'、'3' 三个字符,占三个字节
表格形式
字符**'1''2''3'*ASCII(十进制)4950*51*ASCII(二进制)0011000100110010*00110011**
二进制数据
二进制数据是字节序列,数字123的二进制表示是01111011,如果用二进制格式形式存储,char、short、int、long都可以存储123
存储方式分别如下:
char 存储
01111011
short 存储
00000000 01111011
int 存储
00000000 00000000 00000000 01111011
long 存储
00000000 00000000 00000000 00000000 00000000 00000000 00000000 01111011
文本文件和二进制文件
- 按文本格式存放数据的文件称为文本文件或ASCII文件,文件可以用vi和记事本打开,看到的都是ASCII字符
- 按二进制格式存放数据的文件称为二进制文件,如果用 vi/vim 打开二进制文件,看到的是乱码
二、文件的打开和关闭
对文件进行操作之前必须先“打开”文件,操作(读和写)完成后,再“关闭”文件
文件指针
- 操作文件的时候,C语言为文件分配一个信息区,该信息区包含文件描述信息、缓冲区位置、缓冲区大小、文件读写到的位置等基本信息,这些信息用一个结构体来存放(struct _IO_FILE),FILE结构体和对文件操作的库函数在 stdio.h 头文件中声明的
- 打开文件的时候,fopen 函数中会动态分配一个FILE结构体大小的内存空间,并把FILE结构体内存的地址作为函数的返回值,程序中用FILE结构体指针存放这个地址
- 关闭文件的时候,fclose 函数除了关闭文件,还会释放FILE结构体占用的内存空间
- FILE结构体指针习惯称为文件指针
打开文件
*C语言提供的库函数 fopen 来创建一个新的文件或者打开一个已存的文件,调用fopen函数成功后,返回一个文件指针( FILE )
函数的原型如下:
FILE *fopen( const char * filename, const char * mode );
- 参数 filename 是字符串,表示需要打开的文件名,可以包含目录名,如果不包含路径就表示程序运行的当前目录
- 参数 mode 也是字符串,表示打开文件的方式(模式),打开方式有很多种,我们来看一个表格
方式
含 义
说 明
r
只读
文件必须存在,否则打开失败
w
只写
如果文件存在,则清除原文件内容;如果文件不存在,则新建文件
a
追加只写
如果文件存在,则打开文件,如果文件不存在,则新建文件
r+
读写
文件必须存在。在只读 r 的基础上加 '+' 表示增加可写的功能
w+
读写
在只写w的方式上增加可读的功能
a+
读写
在追加只写a的方式上增加可读的功能
小细节
- 不用去死记硬背 read简写r、write简写w、append简写a罢了
- 有的说打开文本文件的方式要用"rt"、"wt"、"at"、"rt+"、"wt+"、"at+","t"是text的简写,"t"可以省略不写
- 有的说打开二进制文件的方式要用"rb"、"wb"、"ab"、"rb+"、"wb+"、"ab+","b"是binary的简写
关闭文件
fclose 库函数用于关闭文件
函数的原型:
int fclose(FILE *fp);
fp为fopen函数返回的文件指针
举个例子
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
FILE *fp = 0; //定义文件指针fp
// 以只读的方式打开文件
if ( (fp = fopen("/root/cyy1/test83.c","r")) ==0 )
{
printf("文件打开失败\n");
return -1;
}
fclose(fp);
return 0;
}
注意事项
- 调用 fopen 打开文件的时候,一定要判断返回值,如果文件不存在、或没有权限、或磁盘空间满了,都有可能造成打开文件失败
- 文件指针是调用 fopen 的时候,系统动态分配了内存空间,函数返回或程序退出之前,必须用 fclose 关闭文件指针,释放内存,否则后果严重
- 如果文件指针是空指针或野指针,用 fclose 关闭它相当于操作空指针或野指针,后果严重
三、文本文件的读写
向文件中写入数据
C语言向文件中写入数据库函数有 fputc 、fputs、fprintf,这里只介绍fprintf,如果感兴趣的可以去了解一下
fprintf函数的声明如下:
int fprintf(FILE *fp, const char *format, ...);
fprintf函数的用法与printf相同,只是多了第一个参数文件指针罢了,表示把数据输出到文件
举个例子
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
FILE *fp = 0; //定义文件指针fp
// 以只写的方式打开文件
if ( (fp=fopen("/root/cyy1/test84.txt","w")) ==0 )
{
printf("文件打开失败\n");
return -1;
}
int i = 0;
for (i = 0;i < 3 ;i++)
{
fprintf(fp,"这是第%d名同学\n",i+1);
}
fclose(fp);
return 0;
}
运行效果
可以看到 /root/cyy1/test84.txt 中有3行记录,程序 test84.c 不管执行多少次,记录都是3行,因为文件打开的方式是 "w",每次打开文件的时候都会清空原文件中的记录
从文件中读取数据
从文件中读取数据的库函数有 fgetc 、fgets、fscanf,这里只介绍 fgets 就可以了,感兴趣的可以自己去了解一下
fgets函数的原型如下:
char *fgets(char *buf, int size, FILE *fp);
fgets的功能是从文件中读取一行
- 参数 buf 是一个字符串,用于保存从文件中读到的数据
- 参数 size 是打算读取内容的长度
- 参数 fp 是待读取文件的文件指针
- 如果文件中将要读取的这一行的内容的长度小于size,fgets函数就读取一行,如果这一行的内容大于等于size,fgets函数就读取size-1字节的内容
- 调用fgets函数如果成功的读取到内容,函数返回buf,如果读取错误或文件已结束,返回空,即0。如果fgets返回空,可以认为是文件结束而不是发生了错误,因为发生错误的情况极少出现
举个例子
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
FILE *fp = 0; //定义文件指针fp
char buff[301]; // 存放从文件中读取到的一行的内容
memset(buff,0,sizeof(buff));
// 以只写的方式打开文件
if ( (fp=fopen("/root/cyy1/test84.txt","r")) ==0 )
{
printf("文件打开失败\n");
return -1;
}
// 逐行读取文件的内容,输出到屏幕
while (1)
{
if ( (fgets(buff,301,fp)) ==0 )
break;
printf("%s",buff);
}
fclose(fp);
return 0;
}
运行效果
注意事项
- 在读取到 size-1 个字符之前如果出现了换行,或者读到了文件末尾,则读取结束
- 不管 size 的值多大,fgets函只读取一行数据,不能跨行
- 可以将 size 的值设置地足够大,确保每次都能读取到一行完整的数据
四、二进制文件的读写
- 二进制文件没有行的概念,没有字符串的概念
- 我们把内存中的数据结构直接写入二进制文件,读取的时候,也是从文件中读取数据结构的大小一块数据,直接保存到数据结构中。这里所说的数据结构不只是结构体,是任意数据类型
向文件中写入数据
fwrite函数用来向文件中写入数据块,它的原型为:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
参数
- ptr -- 这是指向要被写入的元素数组的指针
- size -- 这是要被写入的每个元素的大小,以字节为单位
- nmemb -- 这是元素的个数,每个元素的大小为 size 字节
- stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流
返回值
如果成功,该函数返回一个 size_t 对象,表示元素的总数,该对象是一个整型数据类型。如果该数字与 nmemb 参数不同,则会显示一个错误
举个例子
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Student
{
char name[51];
int age;
int height;
};
int main()
{
FILE *fp = 0; //定义文件指针fp
struct Student stu;
// 以只写的方式打开文件
if ( (fp=fopen("/root/cyy1/test86.txt","w")) ==0 )
{
printf("文件打开失败\n");
return -1;
}
strcpy(stu.name,"小酋");
stu.age = 20;
stu.height = 168;
fwrite(&stu,sizeof(stu),1,fp);
strcpy(stu.name,"小秦");
stu.age = 21;
stu.height = 170;
fwrite(&stu,sizeof(stu),1,fp);
fclose(fp);
return 0;
}
从文件中读取数据
fread函数用来从文件中读取数据块,它的原型为:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
参数
- ptr -- 这是指向带有最小尺寸 sizenmemb* 字节的内存块的指针
- size -- 这是要读取的每个元素的大小,以字节为单位
- nmemb -- 这是元素的个数,每个元素的大小为 size 字节
- stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流
返回值
调用fread函数如果成功的读取到内容,函数返回读取到的内容的字节数,如果读取错误或文件已结束,返回空,即0。如果fread返回空,可以认为是文件结束而不是发生了错误
举个例子
读刚刚 fwrite 那个文件
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Student
{
char name[51];
int age;
int height;
};
int main()
{
FILE *fp = 0; //定义文件指针fp
struct Student stu;
// 以只读的方式打开文件
if ( (fp=fopen("/root/cyy1/test86.txt","rb")) ==0 )
{
printf("文件打开失败\n");
return -1;
}
while (1)
{
if ( (fread(&stu,sizeof(stu),1,fp)) == 0) break;
printf("姓名:%s 年龄:%d 身高:%d\n",stu.name,stu.age,stu.height);
}
fclose(fp);
return 0;
}
运行效果
五、文件定位
- 在文件内部有一个位置指针,用来指向文件当前读写的位置。在文件打开时,如果打开方式是r和w,位置指针指向文件的第一个字节,如果打开方式是a,位置指针指向文件的尾部。每当从文件里读取n个字节或文件里写入n个字节后,位置指针也会向后移动n个字节
- 文件位置指针与C语言中的指针不是一回事。位置指针仅仅是一个标志,表示文件读写到的位置,不是变量的地址。文件每读写一次,位置指针就会移动一次,它不需要您在程序中定义和赋值,而是由系统自动设置
- C语言提供了 ftell 、rewind 和 fseek 三个库函数来实现文件定位功能
ftell函数
ftell函数用来返回当前文件位置指针的值,这个值是当前位置相对于文件开始位置的字节数
声明
long int ftell(FILE *stream)
rewind函数
rewind函数用来将位置指针移动到文件开头
声明
void rewind ( FILE *fp );
fseek函数
fseek函数用来将位置指针移动到任意位置
声明
int fseek(FILE *stream, long int offset, int whence)
参数
- stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流
- offset -- 这是相对 whence 的偏移量,以字节为单位
- whence -- 这是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一:
常量描述SEEK_SET (也可以用0表示)文件的开头SEEK_CUR (也可以用1表示)文件指针的当前位置SEEK_END (也可以用2表示)文件的末尾
返回值
如果成功,则该函数返回零,否则返回非零值
举个例子
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
FILE *fp = 0; //定义文件指针fp
// 以只读的方式打开文件
if ( (fp=fopen("/root/cyy1/book1.txt","w+")) ==0 )
{
printf("文件打开失败\n");
return -1;
}
fprintf(fp,"hello world\n");
fseek(fp,6,SEEK_SET);
fprintf(fp,"xiaoqiu\n");
fclose(fp);
return 0;
}
运行效果
最初程序创建文件和写入hello world,但是之后我们在第6个位置重置了写指针,并使用 fprintf 语句来重写文件,所以 book1.txt 里面的内容是 hello xiaoqiu
当然 fseek 还可以这样玩
fseek(fp,100,0); // 从文件的开始位置计算,向后移动100字节
fseek(fp,100,1); // 从文件的当前位置计算,向后移动100字节
fseek(fp,-100,2); // 从文件的尾部位置计算,向前移动100字节
六、文件缓冲区
在操作系统中,存在一个内存缓冲区,当调用fprintf、fwrite等函数往文件写入数据的时候,数据并不会立即写入磁盘文件,而是先写入缓冲区,等缓冲区的数据满了之后才写入文件。还有一种情况就是程序调用了 fclose 时也会把缓冲区的数据写入文件
如果想把缓冲区的数据立即写入文件,可以调用 fflush 库函数,它的声明如下:
int fflush(FILE *fp);
函数的参数只有一个,即文件指针,返回0成功,其它失败
七、标准输入、标准输出和标准错误
Linux操作系统为每个程序默认打开三个文件,即标准输入stdin、标准输出stdout和标准错误
输出stderr,其中0就是stdin,表示输入流,指从键盘输入,1代表stdout,2代表stderr
例如
printf("Hello xiaoqiu.\n");
等同于
fprintf(stdout,"Hello xiaoqiu.\n");
简单了解一下即可
版权归原作者 易点点心动 所有, 如有侵权,请联系我们删除。