目录

前言

一、文本数据和二进制数据

文本数据

二进制数据

文本文件和二进制文件

二、文件的打开和关闭

文件指针

打开文件

小细节

关闭文件

举个例子

注意事项

三、文本文件的读写

向文件中写入数据

举个例子

运行效果

从文件中读取数据

举个例子

运行效果

注意事项

四、二进制文件的读写

向文件中写入数据

举个例子

从文件中读取数据

举个例子

运行效果

五、文件定位

ftell函数

rewind函数

fseek函数

举个例子

运行效果

六、文件缓冲区

七、标准输入、标准输出和标准错误


前言

  1. 对计算机而言,一切皆数据,C语言源代码文件是数据、编译后的可执行程序也是数据,数据的存放方式有很多种,如内存文件数据库等,文件是极其重要的一种
  2. 根据文件中数据组织形式的不同,可以把文件分为文本文件二进制文件,C语言源代码是文本文件,编译后的可执行程序是二进制文件

一、文本数据和二进制数据

文本数据

文本数据由字符串组成,存放了每个字符的 ASCII 码值,每个字符占一个字节,每个字节存放一个字符

比如数字 123,如果用文本格式存放,数据内容是’1’、’2’、’3′ 三个字符,占三个字节

表格形式

字符‘1’‘2’‘3’
ASCII(十进制)495051
ASCII(二进制)001100010011001000110011

二进制数据

二进制数据是字节序列,数字123的二进制表示是01111011,如果用二进制格式形式存储,char、short、int、long都可以存储123

存储方式分别如下:

char 存储

01111011

short 存储

0000000001111011

int 存储

00000000000000000000000001111011

long 存储

0000000000000000000000000000000000000000000000000000000001111011

文本文件和二进制文件

  1. 按文本格式存放数据的文件称为文本文件或ASCII文件,文件可以用vi和记事本打开,看到的都是ASCII字符
  2. 按二进制格式存放数据的文件称为二进制文件,如果用 vi/vim 打开二进制文件,看到的是乱码

二、文件的打开和关闭

对文件进行操作之前必须先“打开”文件,操作(读和写)完成后,再“关闭”文件

文件指针

  1. 操作文件的时候,C语言为文件分配一个信息区,该信息区包含文件描述信息、缓冲区位置、缓冲区大小、文件读写到的位置等基本信息,这些信息用一个结构体来存放(struct _IO_FILE),FILE结构体和对文件操作的库函数在 stdio.h 头文件中声明的
  2. 打开文件的时候,fopen 函数中会动态分配一个FILE结构体大小的内存空间,并把FILE结构体内存的地址作为函数的返回值,程序中用FILE结构体指针存放这个地址
  3. 关闭文件的时候,fclose 函数除了关闭文件,还会释放FILE结构体占用的内存空间
  4. FILE结构体指针习惯称为文件指针

打开文件

C语言提供的库函数 fopen 来创建一个新的文件或者打开一个已存的文件,调用fopen函数成功后,返回一个文件指针(FILE *)

函数的原型如下:

FILE*fopen(constchar*filename,constchar*mode);
  1. 参数 filename是字符串,表示需要打开的文件名,可以包含目录名,如果不包含路径就表示程序运行的当前目录
  2. 参数 mode 也是字符串,表示打开文件的方式(模式),打开方式有很多种,我们来看一个表格

方式

含义

说明

r

只读

文件必须存在,否则打开失败

w

只写

如果文件存在,则清除原文件内容;如果文件不存在,则新建文件

a

追加只写

如果文件存在,则打开文件,如果文件不存在,则新建文件

r+

读写

文件必须存在。在只读 r 的基础上加 ‘+’ 表示增加可写的功能

w+

读写

在只写w的方式上增加可读的功能

a+

读写

在追加只写a的方式上增加可读的功能

小细节

  1. 不用去死记硬背 read简写r、write简写w、append简写a罢了
  2. 有的说打开文本文件的方式要用”rt”、”wt”、”at”、”rt+”、”wt+”、”at+”,”t”是text的简写,”t”可以省略不写
  3. 有的说打开二进制文件的方式要用”rb”、”wb”、”ab”、”rb+”、”wb+”、”ab+”,”b”是binary的简写

关闭文件

fclose 库函数用于关闭文件

函数的原型:

intfclose(FILE*fp);

fp为fopen函数返回的文件指针

举个例子

#include #include #include int main(){     FILE *fp = 0; //定义文件指针fp    // 以只读的方式打开文件        if ( (fp = fopen("/root/cyy1/test83.c","r")) ==0 )    {       printf("文件打开失败\n");       return -1;    }    fclose(fp);    return 0; }

注意事项

  1. 调用 fopen 打开文件的时候,一定要判断返回值,如果文件不存在、或没有权限、或磁盘空间满了,都有可能造成打开文件失败
  2. 文件指针是调用 fopen 的时候,系统动态分配了内存空间,函数返回或程序退出之前,必须用 fclose 关闭文件指针,释放内存,否则后果严重
  3. 如果文件指针是空指针或野指针,用 fclose 关闭它相当于操作空指针或野指针,后果严重

三、文本文件的读写

向文件中写入数据

C语言向文件中写入数据库函数有 fputcfputsfprintf,这里只介绍fprintf,如果感兴趣的可以去了解一下

fprintf函数的声明如下:
intfprintf(FILE*fp,constchar*format,...);

fprintf函数的用法与printf相同,只是多了第一个参数文件指针罢了,表示把数据输出到文件

举个例子

#include #include #include 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”,每次打开文件的时候都会清空原文件中的记录

从文件中读取数据

从文件中读取数据的库函数有 fgetcfgetsfscanf,这里只介绍 fgets 就可以了,感兴趣的可以自己去了解一下

fgets函数的原型如下:

char*fgets(char*buf,intsize,FILE*fp);

fgets的功能是从文件中读取一行

  1. 参数 buf 是一个字符串,用于保存从文件中读到的数据
  2. 参数 size 是打算读取内容的长度
  3. 参数 fp 是待读取文件的文件指针
  4. 如果文件中将要读取的这一行的内容的长度小于size,fgets函数就读取一行,如果这一行的内容大于等于size,fgets函数就读取size-1字节的内容

  5. 调用fgets函数如果成功的读取到内容,函数返回buf,如果读取错误或文件已结束,返回空,即0。如果fgets返回空,可以认为是文件结束而不是发生了错误,因为发生错误的情况极少出现

举个例子

#include #include #include 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;}

运行效果

注意事项

  1. 在读取到 size-1 个字符之前如果出现了换行,或者读到了文件末尾,则读取结束
  2. 不管 size 的值多大,fgets函只读取一行数据,不能跨行
  3. 可以将 size 的值设置地足够大,确保每次都能读取到一行完整的数据

四、二进制文件的读写

  1. 二进制文件没有行的概念,没有字符串的概念
  2. 我们把内存中的数据结构直接写入二进制文件,读取的时候,也是从文件中读取数据结构的大小一块数据,直接保存到数据结构中。这里所说的数据结构不只是结构体,是任意数据类型

向文件中写入数据

fwrite函数用来向文件中写入数据块,它的原型为:

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)

参数

  1. ptr— 这是指向要被写入的元素数组的指针
  2. size— 这是要被写入的每个元素的大小,以字节为单位
  3. nmemb— 这是元素的个数,每个元素的大小为 size 字节
  4. stream— 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流

返回值

如果成功,该函数返回一个 size_t 对象,表示元素的总数,该对象是一个整型数据类型。如果该数字与 nmemb 参数不同,则会显示一个错误

举个例子

#include #include #include 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)

参数

  1. ptr— 这是指向带有最小尺寸size*nmemb字节的内存块的指针
  2. size— 这是要读取的每个元素的大小,以字节为单位
  3. nmemb— 这是元素的个数,每个元素的大小为 size 字节
  4. stream— 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流

返回值

调用fread函数如果成功的读取到内容,函数返回读取到的内容的字节数,如果读取错误或文件已结束,返回空,即0。如果fread返回空,可以认为是文件结束而不是发生了错误

举个例子

读刚刚 fwrite 那个文件

#include #include #include 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;}

运行效果

五、文件定位

  1. 在文件内部有一个位置指针,用来指向文件当前读写的位置。在文件打开时,如果打开方式是r和w,位置指针指向文件的第一个字节,如果打开方式是a,位置指针指向文件的尾部。每当从文件里读取n个字节或文件里写入n个字节后,位置指针也会向后移动n个字节
  2. 文件位置指针与C语言中的指针不是一回事。位置指针仅仅是一个标志,表示文件读写到的位置,不是变量的地址。文件每读写一次,位置指针就会移动一次,它不需要您在程序中定义和赋值,而是由系统自动设置
  3. C语言提供了 ftellrewindfseek 三个库函数来实现文件定位功能

ftell函数

ftell函数用来返回当前文件位置指针的值,这个值是当前位置相对于文件开始位置的字节数

声明

long int ftell(FILE *stream)

rewind函数

rewind函数用来将位置指针移动到文件开头

声明

voidrewind(FILE*fp);

fseek函数

fseek函数用来将位置指针移动到任意位置

声明

int fseek(FILE *stream, long int offset, int whence)

参数

  1. stream— 这是指向 FILE 对象的指针,该 FILE 对象标识了流
  2. offset— 这是相对 whence 的偏移量,以字节为单位
  3. whence— 这是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一:
常量描述
SEEK_SET(也可以用0表示)文件的开头
SEEK_CUR (也可以用1表示)文件指针的当前位置
SEEK_END (也可以用2表示)文件的末尾

返回值

如果成功,则该函数返回零,否则返回非零值

举个例子

#include #include #include 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 库函数,它的声明如下:

intfflush(FILE*fp);

函数的参数只有一个,即文件指针,返回0成功,其它失败

七、标准输入、标准输出和标准错误

Linux操作系统为每个程序默认打开三个文件,即标准输入stdin、标准输出stdout和标准错误

输出stderr,其中0就是stdin,表示输入流,指从键盘输入,1代表stdout,2代表stderr

例如

printf("Helloxiaoqiu.\n");

等同于

fprintf(stdout,"Helloxiaoqiu.\n");

简单了解一下即可