目录
实现一个代码,将date.txt拷贝一份生成date2.txt
励志模块
好事总是会发生在下一个转弯
文章重点
(1)为什么使用文件(2)是么是文件(3)文件的打开和关闭(4)文件的顺序读写(5)文件的随机读写(6)文本文件和二进制文件(7)文件读取结束的判定(8)文件缓冲区
一 为什么使用文件
我们想要把内容记录下来,选择删除数据的时候,数据才会不存在,这是关于数据持久化的问题,一般数据持久化的方法有:把数据存放在磁盘文件、存放在数据库等。
使用文件我们可以把文件直接存放到电脑的硬盘上,做到数据的持久化。
二 什么是文件
磁盘上的文件是文件;程序设计中,从文件的功能来分,文件分为数据文件和程序文件。
2.1 程序文件
2.2 数据文件
文件的内容不一定是程序,而是程序运行时读写的数据。比如:程序运行时从中读取的文件或者输出内容的文件。(这篇文章讨论的是数据文件)
(在以前写的文章中,数据的输入输出都是以终端为对象的,就是从终端的键盘输入数据,运行结果显示到显示器上。 但是我们有时候,我们会把信息输出到磁盘上,有需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上的文件。)
2.3 文件名
文件要有一个唯一的文件标识。文件名包括三部分:文件路径+文件名主干+文件后缀。(例如:C:\code\test.txt。C:\code\就是文件路径,test就是文件名主干,.txt就是文件后缀)(为了方便起见,文件标识称为文件名)
三 文件的打开和关闭
使用文件需要打开文件,不使用要关闭文件。
3.1 文件指针
struct _iobuf {
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
};
typedef struct _iobuf FIRE;
FILE * pf ; // 文件指针变量
3.2 文件的打开和关闭
// 打开文件FILE * fopen ( const char * filename , const char * mode );// 关闭文件int fclose ( FILE * stream);
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "w");//默认的路径是test.c的路径,
//""注意是双引号
return 0;
}
下图,test.txt和test.c在同一个路径下
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
//打开文件,读文件
FILE* pf = fopen("test.txt", "r");//默认的路径是test.c的路径,
//""注意是双引号
if (pf == NULL)
{
printf("%s\n", strerror(errno));//如果没有这个文件的话,就会报错,读文件
return 0;
}
//关闭文件
fclose(pf);
pf = NULL;//这个指针变量不再用的时候,置为空指针
return 0;
}
"r"(只读),为了输入数据,打开一个已经存在的文本文件,文件不存在的话,就会报错(返回空指针)。
"w"(只写),为了输出文件,打开一个文本文件,文件不存在的话,建立一个新的文件。(如果没有写文件路径,那么默认在操作文件的路径下,如果文件存在的话,打开的时候,会把里面的内容全部删除)
内存中的数据输出到屏幕或者文件中是输出,反之就是输入
四 文件的顺序读写
fgetc,fputc,注意对象是字符。
fputc
一个一个的写:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("date.txt", "w");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
}
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);
fclose(pf);
pf = NULL;
return 0;
}
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("date.txt", "w");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
}
//fputc('a', pf);
//fputc('b', pf);
//fputc('c', pf);
char ch = 0;
for (ch = 'a'; ch <= 'z'; ch++)
{
fputc(ch, pf);
}
fclose(pf);
pf = NULL;
return 0;
}
内容在文件中显示出来就得打开文件关闭文件,为什么屏幕就不用呢,因为在C语言中,程序运行起来,就会打开三个流:stdio—标准输入流,stdout—标准输出流(在屏幕中显示),stderr—标准错误流(这三个都是FILE*类型的)
所以,上个代码,把pf改成stdout,就可以把内容输出到屏幕上
fgetc
一个一个的读:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("date.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
}
//读文件(一个一个的)
int ch = 0;//fgetc返回的是ASCII码值,用char也可以,但是,当文件结束的时候,返回的是EOF(-1),所以用int
while ((ch = fgetc(pf)) != EOF)
{
printf("%c ", ch);
}// 在这里,pf会自动向后移一位
fclose(pf);
pf = NULL;
return 0;
}
fputs
一行一行的写:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("date.txt", "w");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
}
//一行一行的写
fputs("nihao\n", pf);
fputs("youyoumen\n", pf);//自己加换行符号,再加上,文本输出函数
fclose(pf);
pf = NULL;
return 0;
}
fgets
读一行:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("date.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
}
//一行一行的读
char buf[1000] = { 0 };
fgets(buf, 1000, pf);//读1000-1个,仅仅显示一行。如果这一行的字符不止999个,最次用fgets,就会接着获取后面的内容;返回空指针的时候,文件结束
printf("%s", buf);
fclose(pf);
pf = NULL;
return 0;
}
实现一个代码,将date.txt拷贝一份生成date2.txt
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
//打开
FILE* pr = fopen("date.txt", "r");
if (pr == NULL)
{
printf("open for reading:%s\n", strerror(errno));
return 0;
}
FILE* pw = fopen("date2.txt", "w");
if (pw == NULL)
{
printf("open for writting:%s\n", strerror(errno));
fclose(pr);
pr = NULL;
return 0;
}
//拷贝文件
//读一个拷贝一个
int ch = 0;
while ((ch = fgetc(pr)) != EOF)
{
fputc(ch, pw);
}
//关闭
fclose(pw);
pw = NULL;
fclose(pr);
pr = NULL;
return 0;
}
fprintf
(格式化输出函数)写格式化的数据:写数据到文件里
#include <stdio.h>
#include <string.h>
#include <errno.h>
struct Stu
{
char name[20];
int age;
double d;
};
int main()
{
struct Stu s = { "张三", 20, 95.5 };
FILE* pf = fopen("date.txt", "w");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 0;
}
//写数据
fprintf(pf, "%s %d %lf", s.name, s.age, s.d);
fprintf(stdout, "%s %d %lf", s.name, s.age, s.d);
printf("%s %d %lf", s.name, s.age, s.d);
//关闭
fclose(pf);
pf = NULL;
return 0;
}
fscanf
(格式化输入函数)从文件里读数据
#include <stdio.h>
#include <string.h>
#include <errno.h>
struct Stu
{
char name[20];
int age;
double d;
};
int main()
{
struct Stu s = { 0 };
FILE* pf = fopen("date.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 0;
}
//读数据
fscanf(pf, "%s %d %lf", s.name, &(s.age), &(s.d));
printf("%s %d %lf", s.name, s.age, s.d);
//关闭
fclose(pf);
pf = NULL;
return 0;
}
fwrite
#include <stdio.h>
#include <string.h>
#include <errno.h>
struct Stu
{
char name[20];
int age;
double d;
};
int main()
{
struct Stu s[2] = { {"张三", 20, 95.5},{"李四", 19, 95 } };
FILE* pf = fopen("date.txt", "wb");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 0;
}
//按照二进制的方式写文件,放进二进制信息
fwrite(s, sizeof(struct Stu), 2, pf);//
//关闭
fclose(pf);
pf = NULL;
return 0;
}
fread
#include <stdio.h>
#include <string.h>
#include <errno.h>
struct Stu
{
char name[20];
int age;
double d;
};
int main()
{
struct Stu s[2] = { 0 };
FILE* pf = fopen("date.txt", "rb");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 0;
}
//按照二进制的方式读文件
fread(s, sizeof(struct Stu), 2, pf);//
printf("%s %d %lf", s[0].name, s[0].age, s[0].d);
printf("%s %d %lf", s[1].name, s[1].age, s[1].d);
//关闭
fclose(pf);
pf = NULL;
return 0;
}
知识点:fread的返回值是读取文件内容的块数目,不是文件总大小
4.1 对比一组函数
#include <stdio.h>
#include <string.h>
#include <errno.h>
struct Stu
{
char name[20];
int age;
double d;
};
int main()
{
struct Stu s = { "张三", 20, 95.5 };
struct Stu tmp = { 0 };
char buf[100] = { 0 };
sprintf(buf, "%s %d %lf\n", s.name, s.age, s.d);//把后面的内容,按照格式放到字符串buf中
printf("%s\n", buf);
sscanf(buf, "%s %d %lf", tmp.name, &(tmp.age), &(tmp.d));//把字符串buf里的内容放到后面
printf("%s %d %lf", tmp.name, tmp.age, tmp.d);
//关闭
return 0;
}
scanf、fscanf、sscanf
printf、fprintf、sprintf
scanf 从标准输入流(stdin)上进行格式化输入的函数
printf向标准输出流(stdout)上进行格式化的输出函数
fscanf 可以从标准输入流(stdin)/指定的文件上读取格式化的数据
fprintf把数据按照格式化的方式输出到标准输出流(stdout)/指定的文件
sscanf 可以从一个字符串提取(转化)出格式化数据
sprintf 把一个格式化的数据转化成字符串
五 文件的随机读写
5.1 fseek
int fseek ( FILE * stream , long int offset , int origin );
EILE* stream表示pf
long int offset表示偏移量
int origin有三种选择:SEEK_SET(这个表示起始位置)、SEEK_CUR(这个表示除了起始和末尾的位置)、SEEK_END(这个表示末位的位置)
读:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 0;
}
//读文件
int ch = fgetc(pf);
printf("%c\n", ch);//a
ch = fgetc(pf);
printf("%c\n", ch);//b 如果不加干预的话,下次获取出来的就是c,
//定位指针文件
//用fseek可以直接让pf定位到f
//fseek(pf, 3, SEEK_CUR);//方法一:直接让pf定位到f
//fseek(pf, 5, SEEK_SET);//方法二:也可以直接让pf定位到f,这里的SEEK_SET表示起始位置
fseek(pf, -1, SEEK_END);//方法三:也可以直接让pf定位到f,这里的SEEK_END表示末尾位置
ch = fgetc(pf);
printf("%c\n", ch);//f
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
打印结果;abf
写:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("text.txt", "w");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 0;
}
//写文件
int ch = 0;
for (ch = 'a'; ch < 'z'; ch++)
{
fputc(ch, pf);
}
fseek(pf, -1, SEEK_END);//直接让pf定位到z,这里的SEEK_END表示末尾位置
fputc('#', pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
打印:
5.2 ftell
long int ftell ( FILE * stream ); 返回的是偏移量的值,即偏移量是多少(偏移量从0开始)
5.3 rewind
void rewind ( FILE * stream ); (文件指针回到起始位置)
六 文本文件和二进制文件
字符一律以 ASCII 形式存储,数值型数据既可以用 ASCII 形式存储,也可以使用二进制形式存储。
七 文件读取结束的判断
7.1 被错误使用的feof
在文件读取过程中,不能用 feof 函数的返回值直接用来判断文件的是否结束。而是 应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束 。
(fgetc(字符)fgets(文本)fread(二进制))
八 文件缓冲区
因为有缓冲区的存在, C 语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文 件。 如果不做,可能导致读写文件的问题。
关闭文件,会刷新缓冲区