文件就是电脑C盘里存放东西就叫文件,从功能上我们会把文件分为二种:
而文件的组成又分为三部分:
addins
文件在的位置在C盘
里 Windows
文件中如果想打开一个文件,就会用到文件的类型指针,又叫文件指针(FILE*
)
FILE*
)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结构的变量,这样使用起来更加方便。FILE*
)的使用正常是 FILE* + 变量名
,这样就叫文件指针变量
FILE* pd;
任何一个C程序,运行起来就会默认打开3个流
1、FILE* stdin
(标准输入流) ,从键盘上输入数据对应的是 printf
2、FILE* stdout
(标准输出流),打印数据到屏幕上对应的是 scanf
3、FILE* stderr
(标准错误流)打引错误信息到显示器
流可以理解为输入/输出缓冲区
文件打开(fopen)函数参数
FILE * fopen ( const char * filename, const char * mode );
filename
:是要打开文件的文件名mode
:是文件要用什么方式进行打开文件的打开方式:
文件的打开方式 | 描述 | 如果文件不存在 |
---|---|---|
“r” (只读) | 打开一个已经存在的文本文件,读取数据 | 出错 |
“w” (只写) | 打开一个文本文件,输出数据 | 建立一个新的文件 |
“a”(追加) | 向文本文件尾添加数据 | 建立一个新的文件 |
“rb”(读取) | 输入数据,打开一个二进制文件 | 出错 |
“wb”(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
“ab”(追加) | 向一个二进制文件尾添加数据 | 出错 |
“r+”(读写) | 为了读和写,打开一个文本文件 | 出错 |
“w+”(读写) | 为了读和写,新建一个新的文件 | 建立一个新的文件 |
“a+”(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新的文件 |
“rb+”(读写) | 为了读和写打开一个二进制文件 | 出错 |
“wb+”(读写) | 为了读和写,新建一个新的二进制文件 | 建立一个新的文件 |
“ab+”(读写) | 打开一个二进制文件,在文件尾进行读和写 | 建立一个新的文件 |
文件关闭(fclose)函数的参数
int fclose ( FILE * stream );
stream
:是文件指针,文件使用完后一定要fclose关闭,并把文件指针置空。(和 free 使用方法一样)int main()
{
FILE* pd = fopen("maun.txt", "r");//打开文件
if (pd == NULL)
{
perror("fopen");
return 1;
}
//写文件
........
//关闭文件
fclose(pd);
pd = NULL;
return 0;
}
文件的顺序读写就是按照一定的顺序进行输入或者输出
功能 | 函数名 | 适用于 |
---|---|---|
字符输入函数 | fgetc | 所有输入流 |
字符输出函数 | fputc | 所有输入流 |
文本行输入函数 | fgets | 所有输入流 |
文本行输出函数 | fputs | 所有输入流 |
格式化输入函数 | fscanf | 所有输入流 |
格式化输出函数 | fprintf | 所有输入流 |
二进制输入 | fread | 文件 |
二进制输出 | fwrite | 文件 |
fputc功能:只写一个字符
fputc 输出函数的参数
int fputs ( const char * character, FILE * stream );
fputc 输出函数的使用:
int main()
{
FILE* pd = fopen("moun.txt", "w");// w 是只写
if (pd == NULL)
{
perror("fopen");
return 1;
}
//写文件
//循环的方式进行写
for (char i = 'a'; i <= 'z'; i++)
{
fputc(i , pd);
}
//关闭文件
fclose(pd);
pd = NULL;
return 0;
}
结果:
fgetc 输出函数的参数
int fgetc ( FILE * stream );
fgetc 输入函数的使用:
fgetc功能:只读一个字符
int main()
{
FILE* pd = fopen("moun.txt", "r");// r 是只读一个字符
if (pd == NULL)
{
perror("fopen");
return 1;
}
//读文件
char i = fgetc(pd);
printf("%c\n", i);
//读第二次
i = fgetc(pd);
printf("%c\n", i);
或者用循环的方式进行打印
//char ch = 0;
//while ((ch = fgetc(pd)) != EOF)
//{
// printf("%c ", ch);
//}
//关闭文件
fclose(pd);
pd = NULL;
return 0;
}
结果:
fgets:从文件中读取一行字符到内存中
fputs 函数的参数:
int fputs ( const char * str, FILE * stream );
fputs 函数的使用:
int main()
{
FILE* pd = fopen("moun.txt", "w");// w 是只写
if (pd == NULL)
{
perror("fopen");
return 1;
}
//写一行数据
fputs("Hello China!", pd);
//关闭文件
fclose(pd);
pd = NULL;
return 0;
}
结果:
所有内容销毁
,重新写进入数据
“a”
的形式,“a”
代表追加数据fputs功能:从内存中输出一行字符到文件中
fgets 函数的参数:
char * fgets ( char * str, int num, FILE * stream );
str
:读到的字符串放到str
指向的空间里去num
:读取num-1
个字符,最后一个补上\0
stream
:指向输入流 FILE 对象的指针int main()
{
FILE* pd = fopen("moun.txt", "r");// r是只读
if (pd == NULL)
{
perror("fopen");
return 1;
}
//读一行数据
char arr[10];
fgets(arr, 5, pd);
//打印
printf("%s\n", arr);
//关闭文件
fclose(pd);
pd = NULL;
return 0;
}
结果:
读取成功
返回的str 的地址
,错误
返回的是 NULL
fscanf功能:把文件中的数据格式化的读取到内存中
fscanf 函数的参数:
int fprintf ( FILE * stream, const char * format, ... );
stream
:指向输出流 FILE 对象的变量指针format
:就是和 printf
的输出格式一样fscanf 函数的使用:
struct S
{
char name[20];
int age;
float scores;
};
int main()
{
struct S s = { "zhangsan",23,49.5f };
FILE* pf = fopen("moun.txt", "w");
if (pf == NULL)
{
perror("fopen:");
return 1;
}
fprintf(pf, "%s %d %f", s.name, s.age, s.scores);//打印到txt文件
// 下面的方法在前面 流 中有写
// stdout 是打印到屏幕上 流 的函数和 printf 一样
fprintf(stdout, "%s %d %f", s.name, s.tele, s.scores);
fclose(pf);
pf = NULL;
return 0;
}
结果:
fprintf
是把内容输出到文件上printf
是把内容输出到屏幕上fscanf 输入函数的参数:
int fscanf ( FILE * stream, const char * format, ... );
stream
:指向输入流 FILE 对象的指针fscanf 输入函数的使用:
struct S
{
char name[20];
int age;
float scores;
};
int main()
{
struct S s = { 0 };
FILE* pd = fopen("moun.txt", "r");
if (pd == NULL)
{
perror("fopen");
return 1;
}
//写文件
//从 pd 的文件读数据到结构体 s 里
fscanf(pd,"%s %d %f", s.name, &(s.age), &(s.scores));
//打印到屏幕上
printf("%s %d %f\n", s.name, s.age, s.scores);
或者:
//fprintf(stdout,"%s %d %f\n", s.name, s.age, s.scores);
fclose(pd);
pd = NULL;
return 0;
}
结果:
fprintrf
函数写到文件里的,叫写文件fscanf
是从文件里读数据到内存中,叫读数据fscanf
函数把文件上的数据读到 struct S s
里fwite 函数的参数:
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
fwite 二进制输出的使用:
wb
是以二进制的方式写&s
的里读 1
个 sizeof( struct S)
的大小的数据到 pd
文件里(二进制的方式)fread 函数的参数:
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
fread 二进制输入的使用:
struct S
{
char name[20];
int age;
float scores;
};
int main()
{
struct S s = { "zhengsan",23,49.5f };
//以二进制的方式读到内存中
// rb 是以二进制的方式进行读取
FILE* pd = fopen("moun.txt", "rb");
if (pd == NULL)
{
perror("fopen");
return 1;
}
// 二进制的方式读
fread(&s, sizeof(struct S), 1, pd);
//打印
printf("%s %d %f", s.name, s.age, s.scores);
fclose(pd);
pd = NULL;
return 0;
}
结果:
sprintf
函数的参数:
int sprintf ( char * str, const char * format, ... );
sscanf
函数的参数:
int sscanf ( const char * s, const char * format, ...);
sprintf
和 sscanf
函数数的使用:
struct S
{
char name[20];
int age;
float scores;
};
int main()
{
struct S s = { "zhengsan",23,49.5f };
struct S tmp = { 0 };
char buf[100] = { 0 };
//把s中的格式化数据转换成字符串放到 buf 中
sprintf(buf, "%s %d %f", s.name, s.age, s.scores);
//打印
printf("字符串:%s\n", buf);
//从字符串buf中获取一个格式化的数据到 tmp 中
sscanf(buf, "%s %d %f", tmp.name, &(tmp.age), &(tmp.scores));
printf("格式化:%s %d %f\n", tmp.name, tmp.age, tmp.scores);
return 0;
}
结果:
%d %f
这些就是按照格式化的输出 sprintf
是把一个格式化的数据转换成字符串
sscanf
从一个字符串中转化为一个格式化的数据
scanf:是针对标准输入的格式化输入语句
printf:是针对标准输出的格式化输出语句
fscanf:是针对所有输入流的格式化输入语句
fprintf:是针对所有输出流的格式化输出语句
sscanf:从一个字符串中转化为一个格式化的数据
sprintf:是把一个格式化的数据转换成字符串
前面的文件函数都是按照顺序进行读写的,那么是可以从中间或者从末尾进行读写呢
下面的函数就可以实现
fseek 函数的参数
int fseek( FILE *stream, long offset, int origin );
stream
:对应文件指针offset
:相对于origin参数的偏移量origin
:从那个位置开始进行origin
有三种可能取值:数值 | 位置 |
---|---|
SEEK_SET | 文件开头 |
SEEK_CUR | 文件指针的当前所处的位置 |
SEEK_END | 文件末尾 |
fseek 函数的使用:
int main()
{
//打文件
FILE* pd = fopen("text.txt", "r");
if (pd == NULL)
{
perror("fopen");
return 1;
}
//读文件 定位文件的位置
fseek(pd, 2, SEEK_SET);//跳过 2 个字符
int ch = fgetc(pd);
printf("%c\n", ch);
//从当前的位置在跳过 2 个字符
fseek(pd, 2, SEEK_CUR);
ch = fgetc(pd);
printf("%c\n", ch);
//从末尾的位置向前跳 2 个字符
fseek(pd, -2, SEEK_END);
ch = fgetc(pd);
printf("%c\n", ch);
//关闭文件
fclose(pd);
pd = NULL;
return 0;
}
结果:
注意:上面每次文件读取完毕后,文件指针++ 一次
ftell 函数的参数
long int ftell ( FILE * stream );
结果:
rewind 函数的使用:
void rewind ( FILE * stream );
stream
:对应文件指针结果:
二进制文本:
数据在内存中以二进制的形式存储,如果不加转换的输出到文件里,就叫二进制文件
文本文件:
如果要求在文件上以ASCII码的形式存储,则需要在存储前转换
以ASCII字符的形式存储的文件就是文本文件
一个数据在内存中是怎么存储的呢?
如果是字符,一律以ASCII形式存储
数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储
例如:
如果有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节)
而二进制形式输出,则在磁盘上只占4个字节
下面就是ASCII形式存储 和 二进制形式存储的区别:
下面转化成代码的形式:
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
fclose(pf);
pf = NULL;
return 0;
}
文件是怎么判定文件的结束的呢,是什么情况下才是结束的?
feof 是不能用来判断文件是否结束
feof 是当文件结束后,用来判断是什么原因而结束的,是读取失败还是遇到了文件末尾
fgetc
判断是否为 EOF .fgets
判断返回值是否为 NULL二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:
fread判断返回值是否小于实际要读的个数
如果要读的字符个数是 6 ,真实读到的是 3 个字符,那说明后面没有字符了,就判定结束
如果要读的字符个数是 6 ,真实读到的是 6 个字符,那说明后面还有字符
例如:
文本文件的判断
#include
#include
int main(void)
{
int c; // 注意:int,非char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
if(!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
二进制文件的判断:
#include
enum { SIZE = 5 };
int main(void)
{
double a[SIZE] = {1.,2.,3.,4.,5.};
FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式
fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组
fclose(fp);
double b[SIZE];
fp = fopen("test.bin","rb");
size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组
if(ret_code == SIZE) {
puts("Array read successfully, contents: ");
for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
putchar('\n');
} else { // error handling
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\n");
else if (ferror(fp)) {
perror("Error reading test.bin");
}
}
fclose(fp);
}
用于证明缓冲区存在的代码:
#include
#include
int main()
{
FILE* pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
//注:fflush 在高版本的VS上不能使用了
printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在关闭文件的时候,也会刷新缓冲区
pf = NULL;
return 0;
}
结果:
因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。