为什么会有文件
在写代码的过程中,我们写的数据是存储在电脑的内存中的,当程序结束和退出时,内存回收,我们写的数据也会随执行丢失,当再次运行程序时,是看不到上一次运行程序时的数据的,而文件可将数据进行持久化的保存。
二进制文件和文本文件
数据在文件中存储的形式有2种:
- 数据不加转换直接以二进制的形式存储在文件中,该文件就叫二进制文件
- 在存入文件前,将二进制形式的数据转换成ASCII码值的形式,再存入文件中,该文件就叫文本文件
注意:字符以ASCII码值的形式进行存储,数值型数据可以转换成ASCII码值的形式存储,也可以以二进制的形式进行存储
以1000为例:
1000在文本文件和二进制文件中存储是下面这样的:
VS中如何打开文本文件:
vs中如何打开二进制文件:
流和标准流
- 流的概念
当我们将数据输出到各种外部设备,或者从各种外部设备中输入数据时,因设备不同,输出和输入的操作也就不同,为方便程序员操作,就提出了个抽象的概念流,我们可以想象成河流,一条运输字符的河流。
下面借助画图板来方便大家理解:
c语言针对文件,画面,键盘等将数据进行输入或输出都是通过流操作的,对数据进行输入或输出时,都要先打开流,之后再关闭流。
- 标准流
当我们在键盘上输入数据,在显示屏上输出数据时,其实已经在使用流了,在c程序中,用scanf函数输入和printf函数输出数据时,直接默认打开标准流了。
- stdin——标准输入流,在键盘上输入数据时,默认打开stdin,scanf函数就可在打开的标准流中输入数据。
- stdout——标准输出流,大多数环境输出至显示屏上,printf函数就是将信息输出到标准输出流中。
- stderr——标准错误流,将出现的错误输出到显示屏上。
默认打开标准输入输出流,是程序员在使用scanf和printf等函数时就可以进行输入和输出的操作了
文件指针
每个被使用的文件(各种外部设备)都在内存中开辟了相应的文件信息区,用来存放文件的信息(如:文件名,文件状态等),这些信息都被保存在一个结构体变量中,该结构体类型是由系统声明的,取名为FILE。
如下面画图板所示:
可以看出,通过访问文件信息区就可以访问相应的文件了
那该如何访问文件信息区呢?我们可以通过创建文件指针变量来访问
FILE *pf;//通过得到文件信息区的地址,来访问文件信息区
在前面所说的标准流:stdin,stdout,stderr,其实它们的类型也为FILE*
从上面介绍得知,当我们从键盘上输入数据,到输出至屏幕时,文件指针stdin,stdout是默认打开的,也就是把我们的流打开,打开之后就可访问我们的外部设备,而当我们想要访问其他各种文件时,我们需要定义一个指向FILE类型数据的指针变量,如:pf,来存放文件信息区的地址,然后通过文件指针变量来间接访问与它相关联的文件。
stdin,stdout,stderr是标准流,系统默认自动打开,而程序员自己定义的文件指针变量不是标准流,需要程序员手动打开
文件的打开和关闭
1. 打开文件
fopen——该函数的功能就是打开文件
函数定义如下:
FILE* fopen(const char* filename,const char* mode);
//第一个参数代表文件名,第二个参数代表文件的打开方式
//文件打开成功,则返回有效指针,否则返回NULL
示例1:以写的方式打开文件
示例2:以读的方式打开文件:
代码在这:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *pf = fopen("test.txt","w");
if(pf == NULL)
{
perror("fopen");//若文件打开失败,则会报错
return 1;
}
// ....写文件
return 0;
}
2. 关闭文件
fclose——该函数的功能是关闭文件
函数定义如下:
void fclose (FILE *stream);
//参数代表要关闭的文件
文件在使用之后,就要关闭文件这是必要的
代码如下:
#include <stdio.h>
#include <stdlib.h>
int main()
{
//打开文件
FILE *pf = fopen("test.txt","w");
if(pf == NULL)
{
perror("fopen");//若文件打开失败,则会报错
return 1;
}
// ....写文件
//关闭文件
fclose(pf);
pf == NULL;//pf一定要置为空指针,不然将会变成野指针,这很危险
return 0;
还有一些其他的打开方式,大家可自行上网查阅。
文件的读写
站在内存的角度看:
- 读文件是从外部设备获取数据内存中
- 写文件是从内存中输出数据到外部设备
下面是一些读文件和写文件所需的函数:
- 示例1:
1.运用fputc(写字符)函数,进行写文件
fputc的定义:
int fputc (int character,FILE *stream);
//第一个参数代表要写的字符
//第二个参数代表要写的文件
运用:
#include <stdio.h>
#include <stdlib.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "w");//注意:进行的是写文件的程序,所以要以写的方式打开文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件
else
{
//将abcd写入文件中
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);
fputc('d', pf);
}
fclose(pf);
pf = NULL;
return 0;
}
结果:
2.运用fgetc(读字符)函数,进行读文件
fgetc定义如下:
int fgetc (FILE *stream);
//将要读的文件写入参数中
//正常时,返回值是读到的字符的ACSII码值
//失败时,返回EOF(文件结束标志)
将刚刚写进去的abcd从文件中读出来
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE* pf = fopen("test.txt", "r");//以读的方式打开文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = 0;//接收返回的ASCII码值
while ((ch = fgetc(pf)) != EOF)
{
printf("%c ", ch);
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
结果:
- 示例2:
1. 运用fputs(写字符串)函数,进行写文件
fputs定义如下:
int fputs (const char* str,FILE *stream);
//第一个参数代表要写的字符串的地址
运用:
int main()
{
//打开文件
FILE * pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件
char arr[] = "abcdefg\n";
fputs(arr, pf);
char str[] = "i am student";
fputs(str, pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
结果:
2. 运用fgtes(读字符串)函数,进行读文件
fgets定义如下:
char * fputs(char* str,int num,FILE * stream);
//第一个参数代表要读到的目标地址
//第二个参数代表要读的个数
//返回的是目标地址的首地址
注意:在进行读字符串时,程序只会读取(num - 1)个字符,最后一个num用来存放‘\0’;在遇到\n时,系统也会进行读取,读取完\n之后,系统会直接在其后添上‘\0’.
代码如下:
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "r");//文件中存放的是abcdefg
//此时文件中放的是abcdefg
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
char arr[20] = "xxxxxxxxxxxxxxxxxx";
fgets(arr, 7, pf);//只读取7个字符
printf("%s", arr);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
监视窗口:
我们可以看到系统只读取了6个字符,最后一个num存放的是‘\0’,并没有去读到我们的字符g
所以输出的结果是:abcdef
当文件中存储的是:
abcde
fg
一共存储了两行字符
代码如下:
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
char arr[20] = "xxxxxxxxxxxxxxxxxx";
fgets(arr, 7, pf);
printf("%s", arr);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
监视窗口:
可知,遇到\n时也会将其读取到内存中
输出结果:abcde
- 示例3:
1. 运用fprintf(将格式化数据写入文件)函数,进行写文件
fprintf的定义:
int fprintf(FILE* stream,const char* format,...);
//第二个参数代表格式化数据
//成功时,返回写入字符的个数,否则返回负数
运用:
struct S
{
char name[20];
int age;
float score;
};
int main()
{
//打开文件
FILE * pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件
struct S s = { "lili",19,98.5f };
fprintf(pf,"%s %d %.1f\n", s.name, s.age, s.score);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
结果:
2. 运用fcanf(将文件中的格式化数据读到文件中)函数,进行读文件
fscanf的定义:
int fscanf(FILE* stream,const char* format,...);
使用:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
//打开文件
FILE * pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
struct S s = { 0 };
fscanf(pf,"%s %d %f", s.name, &(s.age), &(s.score));
printf("%s %d %.1f\n", s.name, s.age, s.score);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
结果:
- 示例4:
1.运用fwrite(将二进制数据写到文件中)函数,进行写文件
fwrite的定义:
size_t fwrite(const void*ptr,size_t size,size_t count,FILE* stream);
//参数意思是,将ptr中count个,大小为size个字节的数据,写到文件中
//返回值是写入的总数
使用:
int main()
{
struct S s = { "cuihua",18,98.2 };
FILE *pf = fopen("test.txt", "wb");
if (pf == NULL)
{
perror(fopen);
return 1;
}
else
{
fwrite(&s, sizeof(struct S), 1, pf);
}
fclose(pf);
pf = NULL;
return 0;
}
结果:以打开二进制文件的方式进行打开
2.运用fread(将二进制数据读到内存中)函数,进行读文件
fread的定义:
size_t fread(void *ptr,size_t size,size_t count,FILE* stream);
使用:
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { 0 };
FILE* pf = fopen("test.txt", "rb");
if (pf == NULL)
{
perror(fopen);
return 1;
}
else
{
fread(&s, sizeof(struct S), 1, pf);
printf("%s %d %.1f\n", s.name, s.age, s.score);
}
fclose(pf);
pf = NULL;
return 0;
}
结果:
文件的随机读写
- fseek——根据文件的起始位置和偏移量来定位文件内容的光标
fseek的定义:
int fseek(FILE* stream,long int offset,int origin);
//第一个参数代表要读写的文件
//第二个参数代表偏移量(可正可负)
//第三个参数代表起始位置
//成功时,返回0,否则返回非0
起始位置分3种情况:
文件中的内容为:下面的三种情况以该文件的内容为例
- SEEK_SET(光标位于文件开头)
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fseek(pf, 4, SEEK_SET);//偏移量为4
int ret = fgetc(pf);
printf("%c\n", ret);
return 0;
}
输出结果:e
- SEEK_CUR(光标位于文件的当前位置)
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fseek(pf, 4, SEEK_SET);//光标位于e的位置
int ret = fgetc(pf);
printf("%c\n", ret);
fseek(pf, 1, SEEK_CUR);从e的位置开始偏移
int t = fgetc(pf);
printf("%c\n",t);
return 0;
}
输出结果:e和g
- SEEK_END(光标位于文件末尾)
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fseek(pf, -4, SEEK_END);//光标从末尾开始往前偏移4个偏移量,负数是往前偏移
int ret = fgetc(pf);
printf("%c\n", ret);
return 0;
}
输出结果:d
ftell——计算文件指针相较于文件起始位置的偏移量
(当不知道光标的位置时,可使用该函数)
定义:
long int ftell(FILE *stream);
int main()
{
FILE* pf = fopen("test.txt", "r");
//文件中的内容为abcdefg
if (pf == NULL)
{
perror("fopen");
return 1;
}
fseek(pf, 4, SEEK_SET);//读到的字符是e,读完之后的光标位于e的后面
int ret = fgetc(pf);
printf("%c\n", ret);
long int num = ftell(pf);//所以结果为5
printf("%ld\n", num);
return 0;
}
输出结果:e和5
rewind——让指针回到起始位置
定义:
void rewind(FILE *stream);
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
rewind(pf);//将光标回到文件的起始位置
int ret = fgetc(pf);
printf("%c\n", ret);
return 0;
}
输出结果:a
c程序中对文件的使用的基础就讲到这啦,创作不易,希望能得到大家的喜欢,一起学习吧!