C语言文件操作

文章目录

  • 1. 什么是文件
    • 1.1 程序文件
    • 1.2 数据文件
    • 1.3 文件名
    • 1.4 文件类型
      • 1.4.1 文本文件:
      • 1.4.2 二进制文件
  • 2. 文件缓冲区
  • 3. 文件的打开和关闭
    • 3.1 文件指针
    • 3.2文件的打开和关闭
  • 4. 文件的顺序读写
    • 4.1 字符输入输出函数
      • 4.1.1 fputc 字符输出
      • 4.1.2 fgetc 字符输入
    • 4.2 文本行输入输出函数
      • 4.2.1 fputs 文本行输出
      • 4.2.2 fgets 文本行输入
    • 4.3 格式化输入输出函数
      • 4.3.1 fprintf 格式化输出
      • 4.3.2 fscanf 格式化输入
      • 4.3.3 sprintf 格式化输出
      • 4.3.4 sscanf 格式化输入
      • 几种格式化输入输出函数的区别
    • 4.4 二进制输入输出函数
      • 4.4.1 fwrite 二进制输出
      • 4.4.2 fread 二进制输入
  • 5文件的随机读写
    • 5.1 fseek
    • 5.2 ftell
    • 5.3 rewind
  • 6. 文件读取结束的判定
    • 6.1 被错误使用的feof
      • 6.1.1 **判断文件读取是否结束**
      • 6.1.2 feof的正确使用例子


使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。

1. 什么是文件

在程序设计中,一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。
文件其实就是一种存放数据的介质(媒介)。

1.1 程序文件

一般写代码时候的后缀为.c或者.h这样的文件称之为程序文件

包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境 后缀为.exe)。

1.2 数据文件

程序文件或者其他应用程序可以操作的文件,称为数据文件

本文讨论的为数据文件

1.3 文件名

  • 一个文件要有一个唯一的文件标识,以便用户识别和引用。
  • 文件名包含3部分:文件路径+文件名主干+文件后缀
    • 例如: c:\code\test.txt
  • 为了方便起见,文件标识常被称为文件名。

1.4 文件类型

根据数据的组织形式,数据文件被称为文本文件二进制文件

1.4.1 文本文件:

  • 数据以ASCLL的形式存到文件里的形成叫做文本文件,也叫ASCLL文件
  • 肉眼能看的懂的文件,用记事本打开是一些字符的,就是文本文件
  • 文本文件放的都是文本

1.4.2 二进制文件

  • 一个数据在内存中是怎么存的放到文件里就是怎么存,以二进制的形式存到文件
  • 肉眼看不懂,用记事本打开是一堆乱码的文件,就是二进制文件

总结

  • 文本文件,是给人看的。
  • 二进制文件,是给机器看的。

举个栗子

将10000以二进制的形式存到文件里

#include 

int main()
{
        int a = 10000;

        FILE* pf = fopen("test.txt","wb");//fopen—— 打开test这个文件,
                                        //wb—— 以二进制的形式去写这个文件

        fwrite(&a,4,1,pf);//将a的内容写进去,写1个4个字节的数据,写进pf指向的这个文件里

        fclose(pf);//写完之后就可以关闭这个文件了

        pf = NULL;//关闭完之后,将指针置空

        return 0;
}

FILE*pf 文件指针变量

  • 用记事本打开来看是一堆看不懂的东西。
    在这里插入图片描述
  • 用二进制编辑器打开
    在这里插入图片描述
    当前机器是小端存储,实际转过来是00 00 27 10,转成十进制就正好是10000。
    C语言文件操作_第1张图片

2. 文件缓冲区

  • 当一个程序要读/写文件的时候,系统会自动为这次读/写文件创建一块内存区域。
  • 这块区域被称为文件缓冲区,文件缓冲区又被分为输入缓冲区输出缓冲区
  • 从内存向磁盘输出数据会先送到内存中的输出缓冲区,装满缓冲区之后才一次送到磁盘上。
  • 如果从磁盘上向计算机读入数据,则从磁盘文件中读取数据输入到内存中的输入缓冲区,充满缓冲区之后,再从缓冲区逐个的将数据送到程序数据区(程序变量等)。
  • 缓冲区大小根据C编译系统决定。

C语言文件操作_第2张图片
举个栗子:

while(1)
        {
                sleep (1);
                printf("hello world");
        }

hello world不是一股脑的全部打印出来,而是先放在输出缓冲区,等输出缓冲区满了才一次性打印到屏幕上。
C语言文件操作_第3张图片

3. 文件的打开和关闭

3.1 文件指针

  • 缓冲文件系统中,关键的概念是**“文件类型指针”**, 简称 “文件指针”
  • 每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE.
    C语言文件操作_第4张图片

将struct _iobuf重新取了个名字叫FILE
每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。

FILE* pf 文件指针变量

通过文件指针变量能够找到与它关联的文件

3.2文件的打开和关闭

  • 文件在读写之前应该先打开文件,在使用结束之后应该关闭文件
  • 在编写程序的时候,在打开文件的同时,应该用一个FILE类型的指针来接收返回值
  • ANSIC 规定使用fopen函数来打开文件fclose关闭文件
  • 打开文件如果失败会返回NULL

打开文件:

FILE * pf = fopen(const char * filename,const char * mode);
FILE * pf = fopen(文件名,打开方式);

关闭文件:

int fclose ( pf );

打开方式:

文件使用方式 含义 如果指定文件不存在
“r”(只读) 为了输入数据,打开一个已经存在的文本文件 出错
“w”(只写) 为了输出数据,打开一个文本文件 建立一个新的文件
“a”(追加) 向文本文件尾添加数据 建立一个新的文件
“rb”(只读) 为了输入数据,打开一个二进制文件 出错
“ab”(追加) 向一个二进制文件尾添加数据 出错
“r+”(读写) 为了读和写,打开一个文本文件 出错
“w+”(读写) 为了读和写,新建一个新的文件 建立一个新的文件
“a+”(读写) 打开一个文件,在文件尾进行读写 建立一个新的文件
“rb+”(读写) 为了读和写打开一个二进制文件 出错
“wb+”(读写) 为了读和写,新建一个新的二进制文件 建立一个新的文件
“ab+”(读写) 打开一个二进制文件,在文件尾进行读和写 建立一个新的文件

举个栗子:

int main()
{
        //以只读模式打开文件test.txt
        //相对路径的写法
        //..表示上一级路径
        //.表示当前路径

        FILE* pf = fopen("test.txt","r");
        if(NULL == pf)
        {
                printf("%s\n",strerror(errno));
                return 0;
        }
        //如果打开成功
        //开始读文件
        //关闭文件
        fclose(pf);
        pf = NULL ;

        return 0;
}
No such file or directory

文件打开失败,并没有创建FILE这个结构体,pf里放的就是个NULL。
当前路径底下并没有我想打开的这个文件。
C语言文件操作_第5张图片

4. 文件的顺序读写

  • 按照一定的顺序往文件里写/读取。
  • 做不到不按顺序的读写。
    • 比如abcdef,只能从 a 开始按顺序读写,做不到一上来就对 f 进行读写。

输出是指将数据输出到文件,输入是将文件里的内容输入到程序数据区

功能 函数名 适用于
字符输入函数 fgetc 所有输入流
字符输出函数 fputc 所有输出流
文本行输入函数 fgets 所有输入流
文本行输出函数 fputs 所有输出流
格式化输入函数 fscanf 所有输入流
格式化输出函数 fprintf 所有输出流
二进制输入 fread 文件
二进制输出 fwrite 文件

4.1 字符输入输出函数

4.1.1 fputc 字符输出

输出单个字符到文件

fputc(文件指针)

按顺序往文件里输出h e l l o,fputc函数一次只能输出1个字符。

 		FILE* pfwrite = fopen("Test.txt","w");
        if(NULL == pfwrite)
        {
                printf("%s\n",strerror(errno));
                return 0;
        }
        //写文件—— 输出到文件里
        fputc('h',pfwrite);//将hello 写入prwrite指向的文件
        fputc('e',pfwrite);
        fputc('l',pfwrite);
        fputc('l',pfwrite);
        fputc('o',pfwrite);

        //关闭文件
        fclose(pfwrite);
        pfwrite = NULL ;
}

在这里插入图片描述

4.1.2 fgetc 字符输入

输入单个字符到程序数据区

fgetc(文件指针)

从文件里输入5个字符到程序数据区,fgetc一次只能读入一个字符

FILE* pfread = fopen("Test.txt","r");
        if(NULL == pfread)
        {
                printf("%s\n",strerror(errno));
                return 0;
        }
        //读文件—— 按顺序打印Test.txt文件中的5个字符
        printf("%c\n",fgetc(pfread));//打印一次的字符就不会再打印
        printf("%c\n",fgetc(pfread));
        printf("%c\n",fgetc(pfread));
        printf("%c\n",fgetc(pfread));
        printf("%c\n",fgetc(pfread));

        //关闭文件
        fclose(pfread);
        pfread = NULL ;
h
e
l
l
o

4.2 文本行输入输出函数

4.2.1 fputs 文本行输出

写入一串字符到文件中

char* fputs(char* string,FILE* stream);

 FILE* pf = fopen("Test.txt","w");
        if(NULL == pf)
        {
                printf("%s\n",strerror(errno));
                return 0;
        }

        fputs("hello ",pf);
        fputs("world!",pf);

        fclose(pf);
        pf = NULL;

C语言文件操作_第6张图片
可以看到,hello world!这串字符就很好的写进我的文件里去了。

如果想以换行的形式放进文件中,应该自己主动放\n进去。
C语言文件操作_第7张图片

4.2.2 fgets 文本行输入

读入一行字符

char* fgets(char* string,int n,FILE* stream);

从文件指针指向的文件里,读取n个字符,放到字符串数组string中去。

例如:

char arr[10]= {0};

        FILE* pfread = fopen("Test.txt","r");
        if(NULL == pfread)
        {
                printf("%s\n",strerror(errno));
                return 0;
        }
        fgets(arr,4,pfread);

        printf("%s\n",arr);

        fclose(pfread);
        pfread = NULL;

hel

4.3 格式化输入输出函数

4.3.1 fprintf 格式化输出

可以将各种各样类型的数据输出到文件中。

int fprintf (FILE * stream,const char* format[,argument]…);

对比一下 printf 与 fprintf 之间的区别,可以发现,两个函数只有一个流的区别而已。
普通的 printf 是将数据输出到屏幕上,fprintf 则是输出到文件里,两者的区别仅此而已。
C语言文件操作_第8张图片

例如:

#include 

struct S
{
        int n;
        float score;
        char arr[10];
};
int main()
{
        struct S s = {100,3.14f,"world"};
        FILE* pf = fopen("test.txt","w");
        if(NULL == pf)
        {
                return 0;
        }
        //格式化的形式写入文件
        printf ("%d %f %s",s.n,s.score,s.arr);//输出到屏幕
        fprintf(pf,"%d %f %s",s.n,s.score,s.arr);//输出到文件

        fclose(pf);
        pf = NULL;

        return 0;
}

fprintf就将内容很好的输出到了文件中。

C语言文件操作_第9张图片

4.3.2 fscanf 格式化输入

从文件中读取各种各样类型的数据。

int fscanf (FILE * stream,const char* format[,argument]…);

对比一下 scanf 和 fscanf 之间的区别,
两者的区别仅仅是 scanf 从键盘获取数据,fscanf 则是从文件中获取数据
C语言文件操作_第10张图片
例如:

struct S
{
        int n;
        float score;
        char arr[10];
};
int main()
{
        struct S s = {0};
        FILE* pf = fopen("test.txt","r");
        if(NULL == pf)
        {
                return 0;
        }
        //格式化的形式读文件
        //scanf ("%d %f %s",&(s.n),&(s.score),s.arr);从键盘输入信息
        fscanf(pf,"%d %f %s",&(s.n),&(s.score),s.arr);//从文件输入信息
        printf("%d %f %s\n",s.n,s.score,s.arr);//将获取的信息打印出来看看
 
        fclose(pf);
        pf = NULL;

结果也没什么好说的

100 3.140000 world

4.3.3 sprintf 格式化输出

将格式化的数据输出到字符串中

#include 

struct S
{
        int n;
        float score;
        char arr[10];
};

int main()
{
        struct S s = {100,3.14f,"abcdef"};
        char str[20] = {0};

		//将格式化的数据转换成字符串存储到str
        sprintf(str,"%d %f %s",s.n,s.score,s.arr);/
        printf ("%s\n",str);
        return 0;
}
100 3.140000 abcdef

打印出来的100和3.14就不再是普通的数字了,而是由字符转换过来的数字。

4.3.4 sscanf 格式化输入

从字符串中将数据以格式化的形式输入

例如:

将之前格式化输出到字符串中的数据拿出来:

#include 

struct S
{
        int n;
        float score;
        char arr[10];
};
int main()
{
        struct S s = {100,3.14f,"abcdef"};
        struct S t = {0};
        char str[20] = {0};

        //将格式化的数据转换成字符串存储到str
        sprintf(str,"%d %f %s",s.n,s.score,s.arr);      

        //从str中读取格式化的数据到t中
        sscanf(str,"%d %f %s",&(s.n),&(s.score),s.arr);

        printf("%d %f %s\n",s.n,s.score,s.arr);
        return 0;
}
100 3.140000 abcdef

几种格式化输入输出函数的区别

scanf / fscanf / sscanf
printf / fprintf / sprintf

  • scabf / printf 是针对标准输入流/标准输出流的 格式化 输入/输出语句。
  • fscanf / fprintf 是针对所有输入流/所有输出流的 格式化 输入/输出语句。
  • sscanf / sprintf
    • sscanf 是从字符串中读取格式化的数据。
    • sprintf 是吧格式化的数据输出成(存储到)字符串中。

4.4 二进制输入输出函数

4.4.1 fwrite 二进制输出

将信息以二进制的形式写入文件

size_t fwrite (const void * buffer,size_t size,size_t count,FILE * stream)

buffer :要写入文件的对象的地址
size_t size:要写入文件的这个元素有少个字节。
size_t count:要写 count 个元素进文件
stream:存放要操作的文件地址的地址。

举个栗子:

struct S
{
        char name[20];
        int age;
        double score;
};
int main()
{
        struct S s = {"张三",20,59};
        FILE* pf = fopen("test.txt","wb");//wb —— 二进制输出
        if(NULL == pf)
        {
                return 0;
        }
        //二进制的形式写文件
        fwrite(&s,sizeof(struct S),1,pf);//将s的内容,写的这个元素有32个字节,
                                        //写1个这样的信息,写进pf指向的文件

        fclose(pf);
        pf = NULL;
        return 0;
}

写进文件后就成了这个样子

C语言文件操作_第11张图片

4.4.2 fread 二进制输入

将文件内容以二进制的形式读取出来

size_t fread (const void * buffer,size_t size,size_t count,FILE * stream)

形式和fwrite一样,只不过是将文件的内容读取到buffer了而已。

struct S
{
        char name[20];
        int age;
        double score;
};
int main()
{
        struct S s = {0};

        FILE* pf = fopen("test.txt","rb");//rb —— 二进制输入

        if(NULL == pf)
        {
                return 0;
        }

        //二进制的形式读文件
        fread(&s,sizeof(struct S),1,pf);//将pf的内容,写的这个元素有32个字节,
                                        //写1个32个字节的数据,写进s这个结构体中

        printf("%s %d %lf\n",s.name,s.age,s.score);
        fclose(pf);
        pf = NULL;

        return 0;
}
张三 20 59.000000

5文件的随机读写

之前也说过,文件的顺序读写只能按照顺序进行读写,abcdef只能从a开始读写,做不到一上来就对 f 进行读写,所以这时候就有了随机读写函数

5.1 fseek

移动文件指针到一个指定的位置。

int fseek ( FILE * stream, long int offset, int origin );

offset:偏移量
origin:文件指针的当前位置
调整指向文件的那个文件指针,以当前位置为起点,根据偏移量来调整。

比如:
C语言文件操作_第12张图片
关于起始位置有三个选择

  1. SEEK_CUR:文件指针的当前位置。
  2. SEEK_END:文件的末尾位置
  3. SEEK_SET:文件的起始位置

用代码尝试一下。
我们先往文件里塞一个 abcdef 进去,一会就从这个文件读取内容。
C语言文件操作_第13张图片

将 e 读取出来。

  • 从文件起始位置:
 int main()
{
        FILE* pf = fopen("Test.txt","r");
        if(NULL == pf)
        {
                return 0;
        }
        //1.定位文件指针
        fseek(pf,4,SEEK_SET);//从文件的起始位置开始偏移4个位置

        //2.读取文件
        int ch = fgetc(pf);
        printf("%c\n",ch);

        fclose(pf);
        pf = NULL;

        return 0;
}
  • 从文件末尾位置。

文件的末尾实际是在最后一个字符的后面,所以要读取到 e 应该移动两格。

#include 
int main()
{
	FILE* pf = fopen("test.txt","r");
	if(NULL == pf)
	{
		return 0;
	}
	//1.定位文件指针
	fseek(pf,-2,SEEK_END);

	//2.读取文件
	int ch = fgetc(pf);
	printf("%c\n",ch);
	fclose(pf);
	pf = NULL;
	return 0;
}
e

5.2 ftell

返回文件指针相对于起始位置的偏移量。

long int ftell ( FILE * stream );

举个栗子:abcdef

int main()
{
        FILE* pf = fopen("test.txt","r");

        if(NULL == pf)
        {
                return 0;
        }
        //1.定位文件指针
        fseek(pf,4,SEEK_SET);//从文件的起始位置开始偏移4个位置
        printf("文件指针相对于文件起始位置的偏移量是 %d\n",ftell(pf));
        fclose(pf);
        pf = NULL;
        return 0;
}
文件指针相对于文件起始位置的偏移量是 4

如果用 fgetc 读取1个字符的话,也相当于文件指针偏移了1,把 a 读过过了就跳过了它。

		fgetc(pf);
        printf("文件指针相对于文件起始位置的偏移量是 %d\n",ftell(pf));
文件指针相对于文件起始位置的偏移量是 1

5.3 rewind

让文件指针的位置回到文件的起始位置。

C语言文件操作_第14张图片

void rewind ( FILE * stream );

int main()
{
        FILE* pf = fopen("test.txt","r");
        if(NULL == pf)
        {
                return 0;
        }
        int ch1 = fgetc (pf);//获取字符a,然后让文件指针往后偏移1位指向b,
        printf("%c\n",ch1);

        rewind(pf);//让文件指针回到文件的起始位置,还是指向a

        int ch2 = fgetc (pf);//还是获取字符a
        printf("%c\n",ch2);

        fclose(pf);
        pf = NULL;

        return 0;
}

结果如此:

a
a

6. 文件读取结束的判定

6.1 被错误使用的feof

牢记

在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。
而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。

feof 的作用就是判断文件结束的原因是什么

6.1.1 判断文件读取是否结束

  • 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )

    • fgetc 判断是否为EOF
    • fgets 判断返回值是否为NULL
    • EOF:文件结束标志,EOF其实是 -1.

例如:如果一个文件里面什么都没有,第一次读取到的就是EOF。
C语言文件操作_第15张图片

	FILE* pf = fopen("test.txt","r");
        if(NULL == pf)
        {
                return 0;
        }

        int ch = fgetc(pf);
        printf("%d\n",ch);

        fclose(pf);
        pf = NULL;
-1

可以知道,文件结束的位置确实放了个EOF在。

  • 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
    • fread 判断返回值是否小于实际要读的个数。

6.1.2 feof的正确使用例子

int main()
{
        FILE* pf = fopen("test.txt","r");
        if(NULL == pf)
        {
                return 0;
        }
        int ch;
        //fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
        while((ch = fgetc(pf)) != EOF)
        {
                putchar(ch);
        }
        //判断是什么原因结束的
        if(ferror(pf))//如果遇到错误的话会返回一个非0值
        {
                printf("error\n");
        }
        else if(feof(pf))//如果是遇到文件结束
        {
                printf("end of file\n");
        }

        fclose(pf);
        pf = NULL;
        return 0;
}
abcdef
end of file

你可能感兴趣的:(c语言,c++,开发语言)