当前位置:网站首页>C语言之文件操作
C语言之文件操作
2022-07-27 14:54:00 【小白菜00】
目录
为什么使用文件
使用文件我们可以将数据直接存放在电脑硬盘上,做到了数据的持久化
在程序设计中,我们一涉及的两种文件
程序文件:包括源文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀名为.exe)
数据文件:文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行时需要从中读取数据的文件,或者输出内容的文件
注意:本章讨论的主要是数据文件
文件名
定义:一个文件有一个唯一的文件标识,以便用户识别和引用
文件名包含3部分:文件路径+文件名主干+文件后缀
eg:c:\code\test,txt
文件指针
定义:缓冲文件系统中,关键的概念是文件类型指针,简称文件指针。
关于FILE
每个被使用的文件都在内存中开辟一个文件信息区,用来存放文件的相关信息(如文件名,文件状态以及文件的当前位置等)。这些信息是保存在一个结构体变量中的,该结构体类型是由系统声明的,取名为FILE
关于VS2013编译环境提供的stdio.h头文件中有以下的文件类型申明
typedef struct _iobuf {
char* _ptr; //文件输出的下一个位置
int _cut; //当前缓冲区的相对位置
char* _base; //指基础位置,文件的起始位置
int _flag; //文件标志
int _file; //文件有效性检查
int _charbuf; //检查缓冲区状况,如果无缓冲区则不读取
int _bufsiz; //文件的大小
char* _tmpfname; //临时文件名
}FILE;注意:
- 不同C编译器的FILE类型包含的内容不完全相同,但是大同小异
- 每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构类型的变量,并填充其中的信息,使用者不必关心细节
- 一般都是通过一个FILE指针来维护这个结构的变量,这样使用起来更加方便
创建一个FILE* 的指针变量
FILE* pf;//文件指针变量定义的pf是一个指向FILE类型数据的指针变量,可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。
通过文件指针变量可以找到与他相关联的文件

流
流:程序数据可能要操作各种各样的硬件(如:硬盘、光盘、u盘等)硬件的不同导致了其读写方式的不同,因为要记硬件读写方式的成本过高,所以就在中间层抽象一个流(其是一个标准)的概念,此时程序向流里面输入数据,然后流再把相应的数据写到我们的不同设备中,因而我们程序员就不用关心硬件读写方式了。
C语言程序只要运行起来,就默认打开了3个流
- stdin——标准输入流——对应键盘
- stdout——标准输出流——对应屏幕
- stderr——标准错误流——对应屏幕
文件的打开和关闭
前言
- 文件在读写之前应该打开文件,在使用结束后应该关闭文件
- 在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也就相当于建立了指针和文件的关系
- ANSIC规定使用fopen函数打开文件,使用fclose函数关闭文件
文件的打开方式

文件打开关闭函数
fopen函数

当用fopen打开一个文件的时候,其会主动创建该文件的文件信息区 ,并且把这个文件信息区的起始地址返回过来,如果打开失败则返回null
参数:filename:传入的文件名 mode:文件的打开方式
fclose函数

注意:使用fclose函数就可以把缓冲区内最后剩余的数据输出到内核缓冲区,并释放文件指针和有关的缓冲区
传入参数为文件类型的指针
返回值:如果成功关闭则返回0,否则返回eof(-1)
使用:
引入头文件:#include <stdio.h>
#include <stdio.h>
void main() {
FILE* pf = fopen("C:\\All\\test.txt", "w");//(绝对路径)打开文件
if (pf==NULL)
{
perror("fopen");
}
fclose(pf);//关闭文件
pf = NULL;
}文件的顺序读写
注意:内存中的数据输出到硬盘中的过程叫写入,硬盘中的数据输入到内存中的过程叫读取

fputc函数

写一个字符到一个流里面,具体要写ASCII码值为c的字符,stream是具体写到哪个地址的文件中
当写入成功时返回写入的字符,当写入失败时返回EOF(文件结束标志)
使用:
引入头文件:#include <stdio.h>
#include <stdio.h>
void main() {
FILE* pf = fopen("test.txt", "w");//打开文件
if (pf==NULL)
{
perror("fopen");
}
char c[] = "hello world";
for (int i = 0; i <= 11; i++)
{
printf("%c",fputc(c[i], pf));
}
fclose(pf);//关闭文件
pf = NULL;
}//hello worldfgetc函数

在stream文件地址处的文件里读取一个字符,如果读取正常就返回该字符的ASCII码值,如果读取失败或遇到EOF则返回EOF
注意:当打开文件并使用fgetc函数读取文件时,fgetc函数从文件的开始位置读取一个字符,每读取一个字符后文件的位置指针向后移动一个字符的位置。若当前读取的是文本文件,当遇到文件结束时返回值为EOF
使用:
引入头文件:#include <stdio.h>
#include <stdio.h>
void main() {
FILE* pf = fopen("test.txt", "r");//打开文件
if (pf==NULL)
{
perror("fopen");
}
//读取文件
printf("%c", fgetc(pf));//h
printf("%c", fgetc(pf));//e
printf("%c", fgetc(pf));//l
printf("%c", fgetc(pf));//l
printf("%c", fgetc(pf));//o
fclose(pf);//关闭文件
pf = NULL;
}fputs函数
![]()
写一个string字符串到stream文件指针所指向的文件中,如果写入成功则返回非0(默认为1),如果写入错误则返回EOF
使用:
引入头文件:#include <stdio.h>
#include <stdio.h>
void main() {
FILE* pf = fopen("test.txt", "w");//打开文件
if (pf==NULL)
{
perror("fopen");
}
//写入文件
fputs("hi", pf);
fputs(" world", pf);
fclose(pf);//关闭文件
pf = NULL;
}//hi world(写入的是1行如果需要换行则需要在字符串中加'\n')fgets函数
![]()
在stream指针所指向的文件中读取最多n个字符放到string地址所指向的空间中,返回该地址的指针,如果读完或出现错误则返回null
注意:如果n=100,那么读取的实际有99个非'\0'字符,最后一个放'\0'
使用:
引入头文件:#include <stdio.h>
#include <stdio.h>
void main() {
FILE* pf = fopen("test.txt", "r");//打开文件
if (pf==NULL)
{
perror("fopen");
}
//读取文件
char arr[6] = { 0 };
printf("%s\n", fgets(arr, 6, pf));//hi wo
printf(arr);//hi wo
printf("%s\n", fgets(arr, 6, pf));//rld
printf(arr);//rld
fclose(pf);//关闭文件
pf = NULL;
//由此观之再次读取时用该数组接受时数组数据被覆盖
}fprintf函数

将agars(参数表)内各项的值,按format(格式控制字符串)所表示的格式,将数据格式为字符串的形式写入到文件指针stream所指向的文件中,fprintf函数返回值是输出的字符数,发生错误时返回一个负值
使用:
引入头文件:#include <stdio.h>
#include <stdio.h>
struct Stu
{
char arr[10];
int num;
float sc;
};
void main() {
FILE* pf = fopen("test.txt", "w");//打开文件
if (pf==NULL)
{
perror("fopen");
}
struct Stu s = { "hello",10,5.5f };
fprintf(pf, "%s %d %f", s.arr, s.num, s.sc);//hello 10 5.500000
fclose(pf);//关闭文件
pf = NULL;
}fscanf函数

将stream指针所指向的文件的数据以format(格式控制字符串)的形式 读取出来,放到argu各种参数中,对于返回值fscanf函数在没有出错的情况下返回实际读取数据的个数,如果出错或者读到结尾则返回EOF
使用:
引入头文件:#include <stdio.h>
#include <stdio.h>
struct Stu
{
char arr[10];
int num;
float sc;
};
void main() {
FILE* pf = fopen("test.txt", "r");//打开文件
if (pf==NULL)
{
perror("fopen");
}
struct Stu s = {0};
fscanf(pf, "%s %d %f", s.arr, &(s.num), &(s.sc));//第一个也可以&(s.arr)没有取址是因为结构体地址与第一个元素地址0偏移
printf("%s %d %f\n", s.arr, s.num, s.sc);//hello 10 5.500000
fclose(pf);//关闭文件
pf = NULL;
}fwrite函数
![]()
作用:将一块内存区域中的数据(buffer所指向的内存区域)以二进制的形式写入到本地文本(stream指向的文件)其返回值返回的是实际写入到文件的基本单元个数
-- buffer:指向数据块的指针
-- size:每个数据的大小,单位为Byte(例如:sizeof(int)就是4)
-- count:数据个数(最多)
-- stream:文件指针
使用:
引入头文件:#include <stdio.h>
#include <stdio.h>
struct Stu
{
char arr[10];
int num;
float sc;
};
void main() {
FILE* pf = fopen("test.txt", "w");//打开文件
if (pf==NULL)
{
perror("fopen");
}
struct Stu s = {"hi",10,5.5f};
//二进制形式写文件
fwrite(&s, sizeof(struct Stu), 1, pf);
fclose(pf);//关闭文件
pf = NULL;
}fread函数
![]()
将stream指针所对应的文件数据读取到buffer指针所对应的内存区域中,size为单个数据的大小,count为数据的个数,该函数的返回值为实际读取到元素的个数,如果发现要读取到完整的元素个数小于指定的元素个数,这就是最后一次读取,读取失败返回0
使用:
引入头文件:#include <stdio.h>
#include <stdio.h>
struct Stu
{
char arr[10];
int num;
float sc;
};
void main() {
FILE* pf = fopen("test.txt", "r");//打开文件
if (pf==NULL)
{
perror("fopen");
}
struct Stu s = {0};
//二进制形式读文件
fread(&s, sizeof(struct Stu), 1, pf);
printf("%s %d %f\n", s.arr, s.num, s.sc);//hi 10 5.500000
fclose(pf);//关闭文件
pf = NULL;
}文件的随机读写
fseek函数
根据文件指针的位置和偏移量来定位文件指针(调整文件指针)

参数:
stream——针对stream文件地址所对应的文件
offset——偏移量【字节为单位】(负数为向左偏移,正数为向右偏移)
origin——起始位置(从哪开始偏移)
关于起始位置有3个参数
- SEEK_SET:文件开头
- SEEK_CUT:当前位置
- SEEK_END:文件结尾
返回值:成功则返回0,否则返回其他值
使用:
引入头文件:#include <stdio.h>
#include <stdio.h>
void main() {
FILE* pf = fopen("test.txt", "r");
if (pf==NULL)
{
perror("fopen");
return;
}
//读取文件
int ch = fgetc(pf);//pf所指向的文件内容为abcdefg
printf("%c\n", ch);
//调整文件指针
fseek(pf, -1, SEEK_CUR);//pf指向的文件,其指针从当前位置向左偏移1字节
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
//关闭文件
fclose(pf);
pf = NULL;
}//没加fseek函数时为abc,加了之后为aab
//注意如果遇到换行的情况,说明前一行的字符串末尾有'\0'其也会记在偏移量中ftell函数
返回文件指针相对于起始位置的偏移量
![]()
rewind函数
让文件指针的位置回到文件的起始位置
![]()
使用:
引入头文件:#include <stdio.h>
#include <stdio.h>
void main() {
FILE* pf = fopen("test.txt", "r");
if (pf==NULL)
{
perror("fopen");
return;
}
//读取文件
int ch = fgetc(pf);//pf所指向的文件内容为abcdefg
printf("%c\n", ch);
//调整文件指针
fseek(pf, 1, SEEK_CUR);//pf指向的文件,其指针从当前位置向左偏移1字节
int a = ftell(pf);//告知文件指针相对于起始位置的偏移量
printf("%d\n",a);//2
rewind(pf);//让文件指针的位置回到文件的起始位置
printf("%d\n",ftell(pf));//0
//关闭文件
fclose(pf);
pf = NULL;
}文本文件和二进制文件
根据数据的组织形式数据文件称为文本文件和二进制文件
二进制文件:数据在内存中以二进制的形式储存,如果不加转换的输出到外存,就是二进制文件
文本文件:如果要求在外存上以ASCII码的形式储存,则需要在储存前转换,以ASCII字符形式储存的文件就是文本文件
一个数据在内存中怎么储存
字符一律以ASCII码形式储存,数值型数据既可以用ASCII码形式储存,也可以用二进制形式储存。
10000储存方式
二进制:(共占用4字节)
- int形式:00000000 00000000 00100111 00010000
ASCII形式:(共占5字节)
- 00110001 00110000 00110000 00110000 00110000
关于ASCII形式:共5字节每个字节都代表一个ASCII码值分别为(1、0、0、0、0)的ASCII码值
文件读取结束的判定
feof函数
应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾部结束
![]()
使用时需引入头文件:#include <stdio.h>
返回值:若达到文件尾部EOF则返回非0;否则返回0
注意:文件结束标志——end of file(EOF——值为-1)
文件缓冲区
理解:


fflush函数
![]()
调用该函数会将缓冲区中的内容写到stream所指向的文件中去(对于内存缓冲区来说是清空,对于硬盘来说是写入),若stream为null,则会将所有打开的文件进行数据更新。
返回值:如果成功刷新则返回0,指定的流没有缓冲区或者只读打开时也返回0,错误返回-1;
使用:
引入头文件:#include <stdio.h>
#include <stdio.h>
#include <Windows.h>
void main() {
FILE* pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠10s-已经写数据了,发现test.txt里没数据\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区,将输出缓冲区的数据写到文件
printf("睡眠10s再次打开test.txt,有内容了。\n");
Sleep(10000);
//关闭文件也会刷新缓冲区,所以才有第二次睡眠,因为要确定刷新缓冲区的作用
fclose(pf);
pf = NULL;
}注意:因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束时关闭文件(文件关闭时也会刷新缓冲区),如果不做可能会导致读写文件的问题。
边栏推荐
- Implementation of filler creator material editing tool
- Servlet用Cookie实现用户上次登录时间
- Clear understanding of torchvision (mind map)
- mvc和mvp和mvvm的区别
- Gpt-2 text generation
- Scala for loop (loop guard, loop step size, loop nesting, introducing variables, loop return value, loop interrupt breaks)
- Layoff quarrel, musk: I'm too hard; Mercilessly open source a public opinion acquisition project; Feature engineering is as simple as parameter adjustment?! Nerf boss shouted that he couldn't move; Cu
- Kmeans implementation
- Notes on implementation and acquisition of flowable custom attributes
- Scala branch control (single branch, double branch, multi branch), return value of branch judgment
猜你喜欢
随机推荐
Automatic classification of e-commerce UGC pictures using Baidu PaddlePaddle easydl
Database notes sorting
[paper reading] a CNN transformer hybrid approach for cropclassification using multitemporalmultisensor images
AS更换背景主题以及背景图片
Jupyter creates a virtual environment and installs pytorch (GPU)
Json数据的格式使用
领导:谁再用redis过期监听实现关闭订单,立马滚蛋!
Notes on implementation and acquisition of flowable custom attributes
Kubesphere multi node installation error
除了「加机器」,其实你的微服务还能这样优化
Is low code the future of development? On low code platform
Implementation of ByteDance service grid based on Hertz framework
Crawl common English names
gpt-2 文本生成
Handling of multiple parts with duplicate names and missing parts when importing SOLIDWORK assemblies into Adams
Layoff quarrel, musk: I'm too hard; Mercilessly open source a public opinion acquisition project; Feature engineering is as simple as parameter adjustment?! Nerf boss shouted that he couldn't move; Cu
CCF-201312-1
Get the array list of the previous n days and the previous and subsequent days of the current time, and cycle through each day
Simulation generate report
Supplement - example of integer programming








