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

文章目录

1、为什么使用文件

2、什么是文件

3、文件的打开和关闭

文件的打开

文件的关闭

4、文件的顺序读写

4.1文件读写的特点

4.2fputc、fgetc函数

4.3fgets、fputs函数

4.4fscanf、fprintf函数

5、标准输入输出流stdin、stdout


1、为什么使用文件

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

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

2、什么是文件

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

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

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

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

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

3、文件的打开和关闭

文件的打开

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

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

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

【C语言】文件的打开和关闭,文件的顺序读写_第1张图片

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

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

#include

int main()
{
	FILE* pf = fopen("test.txt", "r");//相对路径
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

    return 0;
}

【C语言】文件的打开和关闭,文件的顺序读写_第2张图片

文件的关闭

        如下,文件关闭使用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函数,把内存里的数据输出到显示器里以方便我们看到。

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

【C语言】文件的打开和关闭,文件的顺序读写_第3张图片

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

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

4.2fputc、fgetc函数

fputc函数

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

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

int fputc ( int character, FILE * stream );

         比如下面代码,在 “//使用” 段里面的循环内容,依次把 'a' -  'z' 放到文件 test.txt 里面。进行第一次循环之前,和下方图片最左边的一样,文件里没有任何内容,光标在首位。执行第一次循环,把字符‘a’写入文件光标所在位置光标后移一位到‘a’的后面。执行第二次循环,把字符‘b’写入光标所在处光标后移一位到‘b’的后面……执行完毕后,光标在‘z’后面。

【C语言】文件的打开和关闭,文件的顺序读写_第4张图片

#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函数

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

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

int fgetc ( FILE * stream );

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

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

【C语言】文件的打开和关闭,文件的顺序读写_第5张图片

#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;
}

【C语言】文件的打开和关闭,文件的顺序读写_第6张图片

4.3fgets、fputs函数

fputs函数

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

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

int fputs ( const char * str, FILE * stream );

        如下代码和运行结果,值得注意的是,换行符要手动添加,否则会一直在某行输入直到该行满了。比如这段代码,连续用了两次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;
}

【C语言】文件的打开和关闭,文件的顺序读写_第7张图片

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);”  这段取消注释之后,监视内存,发现结果如下第二张图片,读取了"oleo\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;
}

 【C语言】文件的打开和关闭,文件的顺序读写_第8张图片

 【C语言】文件的打开和关闭,文件的顺序读写_第9张图片

 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;
}

【C语言】文件的打开和关闭,文件的顺序读写_第10张图片

fscanf函数

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

        和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;
}

【C语言】文件的打开和关闭,文件的顺序读写_第11张图片

        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;
}

【C语言】文件的打开和关闭,文件的顺序读写_第12张图片

        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;
}

【C语言】文件的打开和关闭,文件的顺序读写_第13张图片
         关于文件,本文的讲解就到这里啦!!

你可能感兴趣的:(C语言学习之路,开发语言,c语言)