0


C语言文件操作详解

前言

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

文本文件和二进制文件

  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( const char * filename, const char * 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 库函数用于关闭文件

函数的原型:

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

注意事项

  1. 调用 fopen 打开文件的时候,一定要判断返回值,如果文件不存在、或没有权限、或磁盘空间满了,都有可能造成打开文件失败
  2. 文件指针是调用 fopen 的时候,系统动态分配了内存空间,函数返回或程序退出之前,必须用 fclose 关闭文件指针,释放内存,否则后果严重
  3. 如果文件指针是空指针或野指针,用 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的功能是从文件中读取一行

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

运行效果

注意事项

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

参数

  1. ptr -- 这是指向带有最小尺寸 sizenmemb* 字节的内存块的指针
  2. size -- 这是要读取的每个元素的大小,以字节为单位
  3. nmemb -- 这是元素的个数,每个元素的大小为 size 字节
  4. 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;
}

运行效果

五、文件定位

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

参数

  1. stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流
  2. offset -- 这是相对 whence 的偏移量,以字节为单位
  3. 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");

简单了解一下即可


本文转载自: https://blog.csdn.net/XiaoQiu__/article/details/127335820
版权归原作者 易点点心动 所有, 如有侵权,请联系我们删除。

“C语言文件操作详解”的评论:

还没有评论