1.引言
过去在写C语言程序时,当我们输入完数据后关闭程序,再一次打开时上次写的数据随着我们关闭程序数据所在的空间便被释放了(或者说是还给了操作系统)。
为什么会这样呢?我们所写的代码是在内存上运行的,而存放数据的变量是在内存的静态区或者栈区,静态区和栈区都是宝贵的资源,我们不可能一直占着,我们关闭程序后是要还回去的。那么今天我们就来学习如何保存数据吧!
2.文件
对于文件可以划分为:"程序文件" 和 "数据文件"
程序文件: 我们所写的代码,比如.c,.h为后缀的文件。
数据文件:通俗的来讲就是我们的输入的一些值或信息。
数据文件:“文本文件” 和 “二进制文件”
二进制文件:是把存储在内存上的数据(二进制)不加以转换就存放到外存(文件也可称为磁盘)上。
文本文件:是把存放在内存上数据(二进制)转换成ASCII所对应的字符,然后存放在外存上。
而反过来,在文件中存放1000,对于文本文件是把它的每一个都看作一个字符,‘1’,‘0’,‘0’,‘0’每一个都看成字符的类型,然后将其对应的ASCII对应数值以二进制的补码存入内存中。
而对应的二进制文件就会把它看成一个整型,直接将其转换为二进制对应的补码,将其存入内存中。
对于文本文件,字符类它是以ASCII对应的字符存入文件,而对于数值类可以用ASCII或者二进制的形式存入外存中。
这是文本文件,左边是字符对应其在内存中对应的二进制。
3."流" 与 "标准流"
我们写的程序是怎样从键盘上读取数据的或者怎样在屏幕上输出数据的呢?
对于不同的设备我们要从中读入或者输出数据这未免有一些困难为了解决这个困难,这就要说设计的大佬们抽象出一个概念流,有点像是链接输出与输入的一条流躺着数据的河流,我们只关心流就行,读取或者输入都从流中进行。
关于标准流,这是我查询资料获得的。
标准流通常指的是在计算机程序中用于输入和输出的默认数据流。在大多数操作系统中,有三个标准流:
-
标准输入(stdin):程序默认从标准输入流中读取数据。在大多数情况下,这与用户的键盘输入相关联。
-
标准输出(stdout):程序默认将其输出写入到标准输出流中。通常,这与控制台或终端相关联。
-
标准错误(stderr):用于向用户报告错误或其他信息。与标准输出类似,但通常被重定向到控制台或日志文件,而不是直接与用户交互。
流的类型是 ”FILE*“ 文件指针。我们对文件的维护是依靠文件指针来进行的。
4.文件的读写
4.1.介绍
有了前面的基础,后面的就会很好的理解,对于文件,我们首先是得打开文件->读或者写->关闭文件这么个流程。
文件的打开:
FILE * fopen ( const char * filename, const char * mode );
filename和mode都是一个指针,接受我们输入的文件名,和打开方式,而它的返回值则是文件指针或则流,如果fopen打开失败,就返回NULL成功返回他所维护的流。那么么便有以下的操作。
FILE* pc = fopen("text.txt", "r");
if(pc == NULL)
{
perror("fopen");//如果打开失败打印错误信息。
return 1;
}
//读和写
fclose(pc);//关闭文件
pc = NULL;
r是文件的打开方式,打开方式告诉计算机以何种方式的只读或只写。
了解了打开和关闭文件,那么就来了解一下读和写吧。
4.2.顺序读写
在读写之前我们要明确一下输入和输出从程序中写入到文件是输出,从文件中写入到程序是输入。
fputc
int fputc ( int character, FILE * stream );
把字符写入文件,写入成功后光标下移,第一个是字符的ASCII码值,故用int接受,第二个参数是流,fputc返回值是对应输入字符的ASCII值,如果到达文件的末尾或者错误时返回EOF。
fgetc
int fgetc ( FILE * stream );
从文件中获得字符每读一个字符光标下移一个字符,它的参数是流,返回值为字符对应的ASCII是,如果到达文件的末尾或者错误时返回EOF。
fgets
char * fgets ( char * str, int num, FILE * stream );
从stream流中读取num个字节到字符串str中。
str读取字符串存放的地方,读取的最大的字符数包括\0,如果成功读取了一个字符串,fgets 返回指向该字符串的指针。失败返回NULL。读取后光标下移。
我明明是要求打印3个字节,为啥它是以2个2个的分开的,因为它预留存储\0的空间 。
fputs
int fputs ( const char * str, FILE * stream );
把str中的内容输出到流stream中。
这个函数不写入换行符若要添加可自行写入,同样它在写入的时候不包括\0。
如果写入成功,fputs 返回非负值;如果发生错误,则返回 EOF。例如:
这我以读的模式去写入数据,所以会报错。
fscanf
int fscanf ( FILE * stream, const char * format, ... );
从流("stream")中,读取 "const char * format"(格式化)的数据到"...."中。
和scanf相比只是多了前面的流指针,表示从文件中读取,返回值是读取的个数当它读取失败或者文件末尾,返回EOF。和scanf有些类似,如果占位符是%s它会忽略前面的空格。
fprintf
int fprintf ( FILE * stream, const char * format, … );
把“.....”以“const char * format”格式化的形式输出到流stream中;
函数返回值如果输入返回成功写入的字符数。如果发生错误EOF。
sscanf和sprintf
int sscanf ( const char * s, const char * format, ...);
从字符串“s”中以“const char * format”格式化的形式将数据输入到“...”。
返回值是成功读取的个数当它读取失败时返回EOF。
int sprintf ( char * str, const char * format, ... );
把数据“....”以“const char * format”格式化的形式输出到字符串str中。如果读取成功返回输出的个数,如果失败返回一个负数好像EOF也是-1
这介绍的sscanf和sprintf虽然跟文件没什么区别,但它们的分析有相似性。
scanf - 从标准输入流上读取格式化的数据写入。
fscanf - 从指定的输入流中读取格式化的数据写入。
sscanf - 在字符串中读取格式化的数据写入。
printf - 把数据以格式化的形式打印到标准的输出流中。
fprintf - 把数据以格式化的形式打印在指定的输出流中。
sprintf - 把数据以格式化的形式打印在字符串中。
fwrite
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
把ptr所指向的size大小的内容写入count次到所指向的文件流stream中,如果写入成功,那么返回所写入的次数count,错误或者文件末尾的话那就返回0。
fread
size_t fread(void *ptr, sizet size, size_t nmemb, FILE *stream);
从文件流中读取nmemb个大小为size个字节的数据读取到ptr。如果读取成功后返回nmemb个,失败或者到文件末尾返回0;
4.3.文件的随机读写
fseek
int fseek ( FILE * stream, long int offset, int origin );
第一个参数是流,
第三个是起始位置有三个固定的值SEEK_SET开头,SEEK_CUR当前位置,SEEK_END末尾,
第二个是相对起始位置的偏移量。
其它也是一样。
ftell
返回文件指针相对于起始位置的偏移量
long int ftell ( FILE * stream );
rewind
void rewind ( FILE * stream );
让文件指针回到起始位置
5.文件结束的判定
feof和ferror
int feof ( FILE * stream );
是分析文件在读取结束后是否为到达文件末尾原因造成。如果与流相关联的文件结束指示器被设置,则返回一个非零值。表示到达文件末尾。
int ferror ( FILE * stream );
是分析文件在读取结束后是否为错误原因造成,如果是则返回一个非零值。表示文件错误。
到达文件末尾:
错误原因: