【C语言】文件的打开和关闭,文件的顺序读写 您所在的位置:网站首页 c语言打开文件不关闭 【C语言】文件的打开和关闭,文件的顺序读写

【C语言】文件的打开和关闭,文件的顺序读写

2023-06-05 11:06| 来源: 网络整理| 查看: 265

1、为什么使用文件

       在编写例如通讯录、图书管理系统等程序的时候,所记录的数据,只有程序运行的时候才会有,但是如果结束运行,之前的数据全部没有了,又要重新输入、操作。这样子就很难受,我们在使用类似程序的时候,应该要把数据记录下来,只有自己选择删除的时候,对应信息才会被删除,这就涉及到了数据持久化问题。

        要实现数据持久化可以 把数据存放在磁盘文件中,或者 存放到数据库里面。C语言里使用文件操作,我们就可以把数据放在磁盘文件中,实现数据持久化。

2、什么是文件

在程序设计中,从文件功能来分类,我们一般谈的文件有两种:程序文件、数据文件。

        a.  程序文件:包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。

        b.  数据文件:文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。

        在这里,显而易见,我们要讨论的是数据文件。

        一个文件要有一个唯一标识,以便于用户识别和引用。这个标识俗称“文件名”。文件名包含三个部分:文件路径+文件名主干+后缀。例如:c:\code\test.c    这个文件名里面,文件路径是c:\code\      文件名主干是test      后缀是.c 

3、文件的打开和关闭 文件的打开

         我们要对文件进行操作,首先就要打开文件,然后在文件里面进行数据的增删查改,用完之后关闭文件。下面一行是C语言内置的打开文件的函数,从中可以得知,该fopen函数有两个参数,返回值是FILE*。两个参数里面,第一个参数是文件名,第二个参数是打开文件的方式。

FILE * fopen ( const char * filename, const char * mode );

         根据需求不同,打开文件也有不同的方式,比如我只需要知道这个文件里有什么数据,那么可以用“只读”方式打开文件,打开文件之后,文件里的内容不可更改。又或者我想要在这个文件后面新增内容,原有数据不变,那么可以用“追加”方式打开文件……

         比如下方,我用“只读”方式打开文件(注意fopen的两个参数都是用双引号)。在这里设计FILE*  类型的指针pf来接收fopen函数的返回值,并且要判断pf指针是否为空。

        这里fopen函数的第一个参数,发现并不是完整的文件名,而是 文件名主干+后缀   ,这是因为test.txt文件和这个C语言程序是在一个文件夹下面的,如下图。 如果不是在一个文件夹下面,那么就要用完整的文件名:文件路径+文件名主干+后缀 。前者叫相对路径,后者叫绝对路径。

文件的关闭

如下,文件关闭使用fclose()函数,该函数返回值是int类型,有一个参数。关闭成功,返回0,否则返回EOF(-1)。

int fclose ( FILE * stream );

 如下代码,文件关闭在最后几行代码,用fclose()函数,该函数里面的参数是文件指针。这个函数执行后,test.txt文件被关闭,但是pf指针并不为空,为了避免野指针的情况,把pf指针置为空。

#include int main() { FILE* pf = fopen("test.txt", "r");//相对路径 if (pf == NULL) { perror("fopen"); return 1; } //文件关闭 fclose(pf); pf=NULL; return 0; } 4、文件的顺序读写 4.1文件读写的特点

         文件的读写和之前我们所使用的scanf、printf不一样。从下面图片可以看出。之前我们都是通过scanf函数,把数据从键盘输入到内存里面。然后利用printf函数,把内存里的数据输出到显示器里以方便我们看到。

        但是在文件操作里面。输入、输出函数,一方面有多个;另一方面,打开文件后,对于输入函数,数据是从文件输入到内存里面,对于输出函数,数据是从内存里输出到文件内。

        在这里相信会有一个小疑问:为什么进行文件操作的时候,要打开、关闭文件,而之前的程序使用scanf、printf都不需要打开键盘、显示器,关闭键盘、显示器呢? 这是因为,每一个程序运行的时候,都默认打开了三个流。如下,键盘和屏幕都是默认打开、关闭的,不需要我们手动打开关闭。 

stdin     标准输入流    --键盘 stdout   标准输出流    --屏幕 stderr    标准错误流    --屏幕

4.2fputc、fgetc函数

fputc函数

        fputc是字符输出函数,一次向文本输出一个字符,适用于所有输出流。

        如下,fputc函数有一个int类型的返回值,以及两个参数。第一个参数是要输入文件的那个字符的ASCLL码,第二个参数是文件指针。这个函数的结果是:字符被写入流的内部位置指示器指示的位置,然后自动前进一。指示器可以理解为文件中的“光标”,写入一个字符之后,光标前进1个位置。

int fputc ( int character, FILE * stream );

#include int main() { FILE* pf = fopen("test.txt", "w");//相对路径 if (pf == NULL) { perror("fopen"); return 1; } //使用 for (int i = 0;i < 26;i++) { fputc('a' + i, pf); } //关闭 fclose(pf);//此时pf并不指向NULL pf = NULL; return 0; }

fgetc函数

 fgetc是字符输入函数,一次向内存输入一个字符,适用于所有输入流。

        如下,fgetc函数只有一个参数,返回值是int类型。该函数作用:返回指定流的内部文件位置指示器当前所指向的字符。然后,内部文件位置指示器将前进到下一个字符。

int fgetc ( FILE * stream );

        如下代码和运行结果(test.txt是顺着上一段代码的,现在该文件里面是'a' - 'z'),虽然fgetc函数返回的是一个字符,但是其返回值是int类型,所以也要定义一个int类型的变量ch来接收该返回值。输出的时候用字符的输出格式即可。

        在这里,由于只需要读取文件内的数据,所以用“只读”方式打开文件。打开文件后,光标是在首位,而不是在末尾,如下图。fgetc执行一次,光标后移一位。

#include int main() { FILE* pf = fopen("test.txt", "r");//相对路径 if (pf == NULL) { perror("fopen"); return 1; } int ch = 0; while ((ch=fgetc(pf)) != EOF) { printf("%c ", ch); } //关闭 fclose(pf);//此时pf并不指向NULL pf = NULL; return 0; } 4.3fgets、fputs函数

fputs函数

        fputs函数是文本行输出函数,一次向文本输出一串字符,适用于所有输出流。

        如下,该函数有两个参数,返回值是int类型。第一个参数是要写入文件的字符串的指针,第二个参数是文件指针。该函数作用是:从指定的地址 (str) 开始复制,直到达到终止空字符 ('\0')。此终止空字符不会复制到流中。

        如下代码和运行结果,值得注意的是,换行符要手动添加,否则会一直在某行输入直到该行满了。比如这段代码,连续用了两次fputs,第一次输入hello,第二次输入leo,但是两个字符串是在同一行的。只有手动输入\n才会换行。

#include int main() { FILE* pf = fopen("test1.txt", "w");//相对路径 if (pf == NULL) { perror("fopen"); return 1; } //写入文件,一行一行写 fputs("hello", pf); fputs("leo",pf); //这里并不会换行,所以要换行的话,手动加\n fputs("\nnextline", pf); //关闭 fclose(pf);//此时pf并不指向NULL pf = NULL; return 0; }

fgets函数

        fgets函数是文本行输入函数,一次向内存写入一串字符,适用于所有输入流。

        如下,该函数返回值是char*类型,含有三个参数。该函数第一个参数是指向在其中复制字符串读取的字符数组的指针,第二个函数是读取字符的个数,第三个函数是文件指针。如果读取成功,返回str,否则返回NULL。该函数作用是:从流中读取字符并将其作为 C 字符串存储到 str 中,直到读取 (num-1) 个字符或到达换行符或文件结尾,以先发生者为准。

char * fgets ( char * str, int num, FILE * stream );

         如下,这段代码也是基于上一段的,所以test1.txt文件里面的内容就是上面一张图片的内容。该程序运行的内存监视如下第一张图。成功从pf指向的文件中,读取4个字符,输出到arr数组中。并且第5个字符的位置,放的是'\0'。

        将  “//fgets(arr,9,pf);”  这段取消注释之后,监视内存,发现结果如下第二张图片,读取了"helloleo\n"这一串字符,然后结尾处加了'\0’。但是这段本意是想从文件读取8个字符写入arr,现在却只读取了5个字符。这是因为:到达换行符比读取8个字符先发生。见上面黄色背景的该函数用法。  

#include int main() { FILE* pf = fopen("test1.txt", "r");//相对路径 if (pf == NULL) { perror("fopen"); return 1; } //读文件,一行一行读 char arr[20] = "123456789"; fgets(arr, 5, pf);//读 5-1 = 4 个,最后加的\0,并且最多只读一行。 如果一次未读完一行,那么下次接着读 //fgets(arr,9,pf); printf(arr); //关闭 fclose(pf);//此时pf并不指向NULL pf = NULL; return 0; } 4.4fscanf、fprintf函数

fprintf函数

 fprintf是格式化输出函数,一次向文件输出任意个数的字符,适用于所有输出流。

        如下,该函数返回值是int类型,有三个参数。其第一个参数是文件指针,后面两个参数和printf的格式一摸一样。函数执行成功,返回输出字符的个数,否则返回负数。

int fprintf ( FILE * stream, const char * format, ... );

 如下函数和运行结果,文件里面的内容和预期一样。同样的,换行等内容也需要自己手动添加。

#include struct S { char name[20]; int age; }; //写入结构体数据 int main() { struct S a = { "zhangsan",20 }; FILE* pf = fopen("test2.txt", "w");//相对路径 if (pf == NULL) { perror("fopen"); return 1; } //格式化输出 fprintf(pf, "struct S a:%s %d", a.name, a.age); //关闭 fclose(pf);//此时pf并不指向NULL pf = NULL; return 0; }

fscanf函数

        fscanf是格式化输入函数,一次向内存输入任意个数的字符,适用于所有输入流。

        和fpritnf类似,fscanf也有一个int类型的返回值,三个参数。其中,第一个参数是文件指针,后面两个参数和scanf函数的的格式一样。如果函数执行成功,返回输入的个数。

int fscanf ( FILE * stream, const char * format, ... );

#include struct S { char name[20]; int age; }; int main() { struct S b = { 0 }; FILE* pf = fopen("test2.txt", "r");//相对路径 if (pf == NULL) { perror("fopen"); return 1; } //格式化 fscanf(pf,"%s %d",b.name,&(b.age)); printf("%s %d", b.name, b.age); //关闭 fclose(pf);//此时pf并不指向NULL pf = NULL; return 0; } 5、标准输入输出流stdin、stdout

        对于流(stream),上面六个函数,第一句话最后总会加上一个“适用于所有输入/输出流”,而上文4.1的内容,也提到了,stdin是标准输入流,stdout是标准输出流。那么stdin、stdout也适用于上面的所有函数。如下:

        1、int fputc ( int character, FILE * stream ); fputc函数第二个参数是一个流,使用标准输出流stdout,那么则向标准输出流——屏幕输出字符'a',其运行结果如下图,不过这个一般而言,用处不大。

#include int main() { fputc('a', stdout); return 0; }

        2、char * fgets ( char * str, int num, FILE * stream );   如下,fgets函数第三个参数是一个流,使用标准输入流stdin的时候,就是从键盘输入到str里面。只读取4个字符到temp指向的空间。

#include int main() { FILE* pf = fopen("test5.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } char temp[20] = { 1 }; fgets(temp, 5, stdin); printf("%s", temp); fclose(pf); pf = NULL; return 0; }

 3、例如fprintf,也适用于所有流,所以标准输出流也适用,如下代码和运行结果。

#include int main() { FILE* pf = fopen("test5.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } char name[20] = "zhangsan"; int age = 20; fprintf(pf, "My name is :%s ,I am %d years old.",name,age); fclose(pf); pf = NULL; return 0; }



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有