C语言之文件操作
文章目录
前言
磁盘上的文件是文件,但是在程序设计中,我们一般涉及到的文件有:程序文件和数据文件两种。
程序文件:包括源程序文件(后缀为.c格式的文件),目标文件(Windows环境下后缀为.obj格式的文件),可执行程序文件(Windows环境后缀为.exe格式的文件)。
数据文件:文件的内容不一定是程序,而是程序运行时读写的数据,如,程序运行的时候需要从该文件当中读取相对应的数据,或者接收程序运行时输出的内容到该文件当中去。
接下来我们讨论的内容都是数据文件。
我们处理的数据的输入输出一般都是以终端为对象的,即从终端的键盘输入数据,程序运行结果显示到显示器上;但是实际上,我们有时候会将程序信息输出到磁盘上,需要用到的时候再从磁盘上将数据读取到内存中进行使用,这个过程处理的就是磁盘文件。
环境:VS2019
一、文件相关
1 文件名
用来标识文件的字符串,通常包括文件扩展名字,如.txt
、.jpg
等。
文件名包含三部分:文件路径+文件名主干+文件后缀。
2 文件类型
根据数据的组织形式,数据文件被称为文本文件和二进制文件。
二进制文件:数据在计算机中,即内存中存储都是以二进制形式进行存储的,如果将存储文件不加以转换的输出到外存的话,就是二进制文件。
文本文件:若要求存储在计算机中的文件在外存上要以ASCII码的形式存储,那么就需要在存储前进行相对应的转化,这样以ASCII码值的形式存储的文件就是文本文件。
那么一个数据在内存当中是如何存储的呢?字符型数据用ASCII码形式进行存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
如:有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节。
3 文件缓冲区
ANSIC标准采用”缓冲文件系统“处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块”文件缓冲区“。
从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上,若从磁盘向计算机读入数据,则得先从磁盘文件输出数据到内存缓冲区,然后再从缓冲区逐个将数据送到程序数据区(程序变量等),缓冲区的大小根据C编译系统决定的。
二、文件操作
1 文件指针
每个被使用的文件都在内存中创建了一个相对应的文件信息区,用来存放文件的相关信息,如,文件名字,文件状态和文件当前位置等等。这些信息都是保存在一个结构体变量里面,这个结构体类型是系统声明的,名为FILE
。
struct _iobuf
{
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
};
typedef struct _iobuf FILE;
所以当打开一个文件的时候,系统就会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息即可。
一般都是使用FILE的指针来维护这个FILE结构的变量。
FILE* pf;//文件指针变量
PF是一个指向FILE类型数据的指针变量,可以使pf指向某个文件的文件信息区(结构体变量),通过该文件信息区中的信息去访问该文件,即,通过指针变量就能够找到与它关联的文件。
2 文件的打开和关闭
在对文件操作之前得打开文件,操作完文件之后得关闭文件。
打开文件的时候,在打开文件的同时,会返回一个FILE*的指针变量指向该文件,即建立了指针和文件之间的关系。
注意:ANSIC规定使用fclose函数来关闭fopen打开的文件。
//打开文件
FILE* fopen(const char* filename,const char* mode);
//关闭文件
int fclose(FILE* stream);
文件的打开方式:
文件打开方式 | 含义 | 若指定文件不存在 |
---|---|---|
r | 只读方式打开文本文件 | 出错 |
w 只写方式打开文本文件 | 创建一个新文件 | |
a | 向文本文件尾部继续添加数据 | 出错 |
rb | 只读方式打开二进制文件 | 出错 |
wb | 只写方式打开二进制文件 | 创建一个新文件 |
ab | 向二进制文件末尾添加数据 | 出错 |
r+ | 可读可写打开一个文本文件 | 出错 |
w+ | 可读可写创建一个新文件 | 创建一个新文件 |
a+ | 打开一个文件,在文件尾部进行读写 | 创建一个新文件 |
rb+ | 可读可写打开一个二进制文件 | 出错 |
wb+ | 可读可写打开二进制文件 | 创建一个新文件 |
ab+ | 打开一个二进制文件,在文件尾进行读和写 | 创建一个新文件 |
3 文件的顺序读写
函数名 | 功能 | 适用于 |
---|---|---|
fgetc | 字符输入函数 | 所有输入流 |
fputc | 字符输出函数 | 所有输出流 |
fgets | 文本行输入函数 | 所有输入流 |
fputs | 文本行输出函数 | 所有输出流 |
fscanf | 格式化输入函数 | 所有输入流 |
fprintf | 格式化输出函数 | 所有输出流 |
fread | 二进制输入 | 文件 |
fwrite | 二进制输出 | 文件 |
3.1 fgetc与fputc函数
//功能:从指定的文件流中读取一个字符,并将该字符作为无符号字符转换为int类型返回
//函数原型:
int fgetc(FILE *stream);
//参数含义:
- stream:文件指针,指定了要读取字符的文件流
//返回值:
- 成功:返回值是字符的ASCII码值
- 失败:返回EOF(-1)
//功能:用于向文件中输出一个字符的函数
//函数原型:
int fputc(int ch, FILE *stream);
//参数含义:
- ch:要输出的字符
- stream:文件指针,指向要将该字符输入到的文件
//返回值:
- 成功:返回写入的字符
- 失败:返回EOF(表示文件结束或出错)
3.2 fgets与fputs函数
//功能:从指定的文件流(stream)中读取一行数据,将其存储在字符数组(str)中
//函数原型:
char *fgets(char *str, int size, FILE *stream);
//参数含义:
- str:指向存储字符串的指针
- size:字符数组的长度
- stream:文件指针,用于指定读取数据的文件
//返回值:
- 成功:一个指针,指向存储读取到的字符串的结束位置
- 失败:返回NULL
//功能:将字符串写入到指定的文件流中
//函数原型:
int fputs(const char *str, FILE *stream);
//参数含义:
- ch:要写入的字符串
- stream:文件指针,指向要将该字符输入到的文件
//返回值:
- 成功:非负数
- 失败:返回EOF
3.3 fread与fwrite函数
//功能:从文件中读取数据到内存
//函数原型:
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
//参数含义:
- ptr:指向内存地址的指针,函数将读取的数据存储到该地址
- size:每个读取数据的大小,例如:sizeof(int)
- count:要读取的数据块数量
- stream:文件指针,指向要读取数据的文件
//返回值:
- 成功:实际读取到的有效数据字节数
- 失败:0
//功能:用于向文件中输出一个字符的函数
//函数原型:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
//参数含义:
- ptr:指向要写入的数据的指针
- size:要写入的每个数据项的大小(以字节为单位)
- nmemb:要写入的数据项的数量
- stream:文件指针,指定要写入的文件
//返回值:
- 成功:返回写入的数据项数量
- 失败:返回0
🤔区分:scanf/fscanf/sscanf/printf/fprintf/sprintf
scanf、fscanf、sscanf 是 C 语言中的输入输出函数,用于从标准输入(如键盘)读取数据。
- scanf:格式化输入,根据指定的格式字符串读取数据。
- fscanf:文件格式化输入,根据指定的格式字符串从文件中读取数据。
- sscanf:格式化输入,根据指定的格式字符串从字符串中读取数据。
printf、fprintf、sprintf 是 C 语言中的输出函数,用于将数据按指定的格式输出到标准输出(如屏幕)或其他地方。
- printf:格式化输出,根据指定的格式字符串输出数据。
- fprintf:文件格式化输出,根据指定的格式字符串将数据输出到文件。
- sprintf:格式化输出,根据指定的格式字符串将数据输出到字符串。
4 文件的随机读写
4.1 fseek函数
//功能:根据文件指针的位置和偏移量来定位文件指针
//函数原型:
int fseek(FILE* stream,long int offset,int origin);
//参数含义:
- stream:文件指针,指向要操作的文件。
- offset:偏移量,表示要从文件偏移的距离。
- origin:偏移基准,有以下三个值:
- SEEK_SET:从文件开头偏移,即0
- SEEK_CUR:从当前文件指针位置偏移
- SEEK_END:从文件末尾偏移
//返回值:
- 成功:返回0
- 失败:返回-1
fseek函数例子:
#include <stdio.h>
int main()
{
FILE* pf;
pf = fopen("hello.txt", "wb");
fputs("This is an apple.", pf);
fseek(pf, 9, SEEK_SET);
fputs(" sam", pf);
fclose(pf);
return 0;
}
进入我们存放代码的目录下,查看是否有hello文件即文件的内容:
4.2 ftell函数
//功能:返回文件指针相对于起始位置的偏移量
//函数原型:
long int ftell(FILE* stream);
//参数含义:
- stream:文件指针,指向要操作的文件。
//返回值:
- 成功:返回表示文件指针当前位置的偏移量的一个long类型的值
- 失败:返回-1
对上述fseek创建的文本文件"hello.txt",计算文件的大小。
#include<stdio.h>
int main()
{
FILE* pf;
long len = 0;
pf = fopen("hello.txt", "rb");
if (pf == NULL)
{
perror("Error opening file.");
}
fseek(pf, 0, SEEK_END);
len = ftell(pf);
fclose(pf);
printf("the size of file:%ld bytes.\n", len);
return 0;
}
4.3 rewind函数
//功能:让文件指针的位置回到文件的起始位置,
//函数原型:
void rewind(FILE* stream);
//参数含义:
- stream:文件指针,指向要操作的文件。
rewind函数例子:
#include<stdio.h>
int main()
{
int n;
FILE* pf;
char buf[27];
pf = fopen("hello.txt", "w+");
if (pf == NULL)
{
perror("Error opening file.");
}
for (n = 'A'; n <= 'Z'; ++n)
{
fputc(n, pf);
}
//将文件指针回到文件的起始位置
rewind(pf);
fread(buf, 1, 26, pf);
fclose(pf);
buf[26] = '\0';
puts(buf);
return 0;
}
5 文件结束判断
feof函数:
//功能:让文件指针的位置回到文件的起始位置,
//函数原型:
int feof(FILE *stream);
//参数含义:
- stream:文件指针,指向要操作的文件。
注意:在文件读取过程中,不能用feof函数返回值直接判断文件是否结束。而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。
- 文件读取是否结束,判断返回值是否为EOF,或者NULL
- fgetc判断是否为EOF
- fgets判断返回值是否为NULL
- 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数
- fread判断返回值是否小于实际要读的个数
文本文件结束判断:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int c;
FILE* pf = fopen("hello.txt", "r");
if (pf == NULL)
{
perror("Error opening file.");
return EXIT_FAILURE;
}
//fgetc:当读取失败的时候或者遇到文件结束的时候,都会返回EOF
//标准C I/O循环读取文件
while ((c = fgetc(pf)) != EOF)
{
putchar(c);
}
//对结束原因进行判断
if (ferror(pf))
{
puts("文件读取错误.");
}
else if (feof(pf))
{
puts("成果读取到文件的末尾而结束.");
}
fclose(pf);
return 0;
}
二进制文本结束判断:
#include<stdio.h>
enum { SIZE = 5 };
int main(void)
{
double a[SIZE] = { 1.0,2.0,3.0,4.0,5.0 };
double b = 0.0;
size_t ret_code = 0;
FILE* fp = fopen("hello.bin", "wb"); // 必须用二进制模式
fwrite(a, sizeof(*a), SIZE, fp); // 写 double 的数组
fclose(fp);
fp = fopen("hello.bin", "rb");
// 读 double 的数组
while ((ret_code = fread(&b, sizeof(double), 1, fp)) >= 1)
{
printf("%lf\n", b);
}
if (feof(fp))
{
printf("Error reading hello.bin: unexpected end of file\n");
}
else if (ferror(fp))
{
perror("Error reading hello.bin");
}
fclose(fp);
fp = NULL;
return 0;
}
总结
这块内容就结束啦,一天结束 (๑•̀ㅂ•́)و✧!!!