目录
1. 文本数据和二进制
1.1 文本数据
文本数据由字符串组成,存放了每个字符的 ASCII 码值,每个字符占一个字节,每个字节存放一个字符
比如数字 123,如果用文本格式存放,数据内容是'1'、'2'、'3' 三个字符,占三个字节
表格形式:
1.2 二进制数据
二进制数据是字节序列,数字123的二进制表示是01111011,如果用二进制格式形式存储,char、short、int、long都可以存储123
1.3文本文件和二进制文件
按文本格式存放数据的文件称为文本文件或ASCII文件,文件可以用vi和记事本打开,看到的都是ASCII字符
按二进制格式存放数据的文件称为二进制文件,如果用 vi/vim 打开二进制文件,看到的是乱码
2. 文件的打开和关闭
对文件进行操作之前必须先“打开”文件,操作(读和写)完成后,再“关闭”文件
2.1 文件指针
操作文件的时候,C语言为文件分配一个信息区,该信息区包含文件描述信息、缓冲区位置、缓冲区大小、文件读写到的位置等基本信息,这些信息用一个结构体来存放(struct _IO_FILE),FILE结构体和对文件操作的库函数在 stdio.h 头文件中声明的
打开文件的时候,fopen 函数中会动态分配一个FILE结构体大小的内存空间,并把FILE结构体内存的地址作为函数的返回值,程序中用FILE结构体指针存放这个地址
关闭文件的时候,fclose 函数除了关闭文件,还会释放FILE结构体占用的内存空间
FILE结构体指针习惯称为文件指针
2.2 打开文件
C语言提供的库函数 fopen 来创建一个新的文件或者打开一个已存的文件,调用fopen函数成功后,返回一个文件指针( FILE *)
函数原型如下:
FILE fopen( const char filename, const char * mode );
参数 filename 是字符串,表示需要打开的文件名,可以包含目录名,如果不包含路径就表示程序运行的当前目录
参数 mode 也是字符串,表示打开文件的方式(模式),打开方式有很多种,我们来看一个表格
不用去死记硬背 read简写r、write简写w、append简写a罢了
有的说打开文本文件的方式要用"rt"、"wt"、"at"、"rt+"、"wt+"、"at+","t"是text的简写,"t"可以省略不写
有的说打开二进制文件的方式要用"rb"、"wb"、"ab"、"rb+"、"wb+"、"ab+","b"是binary的简写
2.3 关闭文件
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;
}
2.4 注意事项:
1.调用 fopen 打开文件的时候,一定要判断返回值,如果文件不存在、或没有权限、或磁盘空间满了,都有可能造成打开文件失败
2.文件指针是调用 fopen 的时候,系统动态分配了内存空间,函数返回或程序退出之前,必须用 fclose 关闭文件指针,释放内存,否则后果严重
3.如果文件指针是空指针或野指针,用 fclose 关闭它相当于操作空指针或野指针,后果严重
3. 文本文件的读写
3.1 向文件中写入数据
C语言向文件中写入数据库函数有 fputc 、fputs、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",每次打开文件的时候都会清空原文件中的记录
3.2 从文件中读取数据
从文件中读取数据的库函数有 fgetc 、fgets、fscanf。
fgets函数的原型如下:
char *fgets(char *buf, int size, FILE *fp);
fgets的功能是从文件中读取一行
1.参数 buf 是一个字符串,用于保存从文件中读到的数据
2.参数 size 是打算读取内容的长度
3.参数 fp 是待读取文件的文件指针
4.如果文件中将要读取的这一行的内容的长度小于size,fgets函数就读取一行,如果这一行的内容大于等于size,fgets函数就读取size-1字节的内容
5.调用fgets函数如果成功的读取到内容,函数返回buf,如果读取错误或文件已结束,返回空,即0。如果fgets返回空,可以认为是文件结束而不是发生了错误,因为发生错误的情况极少出现
3.3 注意事项
1.在读取到 size-1 个字符之前如果出现了换行,或者读到了文件末尾,则读取结束
2.不管 size 的值多大,fgets函只读取一行数据,不能跨行
3.可以将 size 的值设置地足够大,确保每次都能读取到一行完整的数据
4. 二进制文件的读写
二进制文件没有行的概念,没有字符串的概念
我们把内存中的数据结构直接写入二进制文件,读取的时候,也是从文件中读取数据结构的大小一块数据,直接保存到数据结构中。这里所说的数据结构不只是结构体,是任意数据类型
4.1 向文件中写入数据
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 参数不同,则会显示一个错误
4.2 从文件中读取数据
fread函数用来从文件中读取数据块
原型为:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
参数
1.ptr -- 这是指向带有最小尺寸 size*nmemb 字节的内存块的指针
2.size -- 这是要读取的每个元素的大小,以字节为单位
3.nmemb -- 这是元素的个数,每个元素的大小为 size 字节
4.stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流
返回值
调用fread函数如果成功的读取到内容,函数返回读取到的内容的字节数,如果读取错误或文件已结束,返回空,即0。如果fread返回空,可以认为是文件结束而不是发生了错误
#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;
}
5.文件定位
1.在文件内部有一个位置指针,用来指向文件当前读写的位置。在文件打开时,如果打开方式是r和w,位置指针指向文件的第一个字节,如果打开方式是a,位置指针指向文件的尾部。每当从文件里读取n个字节或文件里写入n个字节后,位置指针也会向后移动n个字节
2.文件位置指针与C语言中的指针不是一回事。位置指针仅仅是一个标志,表示文件读写到的位置,不是变量的地址。文件每读写一次,位置指针就会移动一次,它不需要您在程序中定义和赋值,而是由系统自动设置
3.C语言提供了 ftell 、rewind 和 fseek 三个库函数来实现文件定位功能
5.1ftell函数
ftell函数用来返回当前文件位置指针的值,这个值是当前位置相对于文件开始位置的字节数
声明:
long int ftell(FILE *stream)
5.2rewind函数
rewind函数用来将位置指针移动到文件开头
声明:
void rewind ( FILE *fp );
5.3fseek函数
fseek函数用来将位置指针移动到任意位置
声明:
int fseek(FILE *stream, long int offset, int whence)
参数
stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流
offset -- 这是相对 whence 的偏移量,以字节为单位
whence -- 这是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一
返回值
如果成功,则该函数返回零,否则返回非零值
#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);//光标在第二行的第6个字节处,下一行代码写入从第二行第七个字节位置开始写 入
fprintf(fp,"xiaoqiu\n");
fclose(fp);
return 0;
}
最初程序创建文件和写入 hello world,但是之后我们在第6个位置重置了写指针,并使用 fprintf 语句来重写文件,所以 book1.txt 里面的内容是 hello xiaoqiu
6.文件缓冲区
在操作系统中,存在一个内存缓冲区,当调用fprintf、fwrite等函数往文件写入数据的时候,数据并不会立即写入磁盘文件,而是先写入缓冲区,等缓冲区的数据满了之后才写入文件。还有一种情况就是程序调用了 fclose 时也会把缓冲区的数据写入文件
如果想把缓冲区的数据立即写入文件,可以调用 fflush 库函数
声明如下:
int fflush(FILE *fp);
函数的参数只有一个,即文件指针,返回0成功,其它失败
7.标准输入、标准输出和标准错误
Linux操作系统为每个程序默认打开三个文件,即标准输入stdin、标准输出stdout和标准错误
输出stderr,其中0就是stdin,表示输入流,指从键盘输入,1代表stdout,2代表stderr
例如:
printf("Hello xiaoqiu.\n");
等同于
fprintf(stdout,"Hello xiaoqiu.\n");
感谢你的阅读,希望对你有所帮助~
欢迎批评指正,共同进步!