C语言基础 Day10 文件(上)

1. 文件概述

文件分为两类,一类是设备文件,如屏幕、键盘、磁盘、网卡、声卡、显示器、扬声器等;还有一类是磁盘文件。磁盘文件又分为文本文件与二进制文件。文本文件中存储的是ASCII码。而二进制文件存储的主要是音频、视频、图片等内容。

前面我们使用的printf函数实际上就是将内容输出到屏幕上,屏幕也就是我们说的标准输出stdout。而我们也会使用scanf函数在键盘上获取输入,键盘也就是我们说的标准输入stdin。除了之外,后面还需要经常使用标准错误stderr,使用perror函数进行使用。perror的使用方法如下:perror("错误名称");程序运行的时候会自动进行字符串拼接,打印出错误的信息,格式为错误名称: 错误的原因

我们前面使用的系统文件就主要是上面三个。

系统文件文件指针编号
标准输入stdin0
标准输出stdout1
标准错误stderr2

这三个系统文件程序执行时由系统默认打开,用户无需定义就可以直接使用。当程序结束后就会自动被关闭。若我们关闭了系统文件则不能正常执行一些功能。关闭文件我们使用fclose(FILE *fp);函数,里面传入文件的指针即可。下面以一个关闭标准输出的例子为例。

#if 0

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <Windows.h>


int main(int argc, char* argv[])
{
	fclose(stdout);

	printf("hello world\n");

	system("pause");
	return 0;
}

#endif

此时就会导致程序无法完成正常的输出。

2. 文件打开与读写

对文件的操作,我们主要分为三步:

  • 第一步打开文件:使用fopen函数。
  • 第二步操作文件:使用文件操作函数,如fputc、fgetc、fputs、fgets、fread、fwrite等。
  • 第三步关闭文件:使用fclose函数。

首先来看看这些函数:

函数名函数原型返回值参数作用tip
fopenFILE * fopen(const char * filename, const char * mode);成功返回文件指针,失败返回NULLfilename: 需要打开的文件名,根据需要加上路径
mode: 打开文件的模式设置
打开文件-
fcloseint fclose(FILE * stream);成功返回0,失败返回-1stream: 文件指针关闭先前使用fopen打开的文件。此作用让缓冲区的数据写入文件中,并释放系统所提供的的文件资源-
fgetcint fgetc(FILE * stream);成功返回读取到的字符,失败返回-1stream: 文件指针从stream指定的文件中读取一个字符-
fputcint fputc(int ch, FILE * stream);成功返回写入的字符,失败返回-1ch: 需要写入文件的字符
stream: 文件指针
将ch写入stream指定的文件中-
fgetschar * fgets(char * str, int size, FILE * stream);成功返回读取到的字符串,失败或者到文件尾返回NULLstr: 字符串
size: 指定最大读取字符串的长度(size-1)
stream: 文件指针
从stream指定的文件内读入字符,保存到str所指定的内存空间,直到出现了换行符、督导文件末尾或是读了size-1个字符为止会自动加上字符串的结尾符号’\0’
fputsint fputs(const char * str, FILE * stream);成功返回0,失败返回-1str: 字符串
stream: 文件指针
将str所指定的字符串写入到stream指定的文件中字符串结尾符号’\0’不写入文件中
freadsize_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);成功返回读取到的内容块数,如果这个值比nmemb小但是大于0,说明读取到了文件的结尾。失败返回0ptr: 存放读取出来数据的内存空间
size: 指定读取文件内容的块数据大小
nmemb: 读取we年的块数
stream: 已经打开的文件指针
以快书记的方式从文件中读取内容-
fwritesize_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);成功写入文件数据的块数目,与nmemb相等,失败返回0ptr: 准备写入文件数据的地址
size: 指定写入文件内容的块数据大小
nmemb: 写入文件的块数
stream: 已经打开的文件指针
以数据块的方式给文件写入内容-
fprintfint fprintf(FILE * stream, const char * format, …);实际写入文件的字符个数,失败返回-1stream: 已经打开的文件
format: 字符串格式,用法与printf一样
根据参数format字符串来转换并格式化数据,然后将结果输出到stream指定的文件中-
fscanfint fscanf(FILE * stream, const char * format, …);参数成功转换的值的个数,失败返回-1stream: 已经打开的文件
format: 字符串格式,用法与scanf一样
从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据-
feofint feof(FILE * stream);到文件末尾返回非0值,没有到文件末尾返回0stream: 文件指针检测是否读取到了文件末尾判断的是最后一次读操作的内容,不是当前位置的内容

在上面这个表格中,涉及到了文件打开的模式设置,接下来看看模式设置有哪一些。

打开模式含义
r 或 rb以只读的方式打开一个文本文件或二进制文件。若文件不存在则报错
w 或 wb以写方式打开文件,若文件存在则清空文件,若文件不存在则创建一个文件
a 或 ab以追加方式打开文件,在末尾添加内容。若文件不存在则创建文件
r+ 或 rb+以可读、可写的方式打开文件,不创建新文件
w+ 或 wb+以可读、可写的方式打开文件,如果文件存在则清空文件,若文件不存在则创建一个文件
a+ 或 ab+以添加方式打开文件,打开文件并在末尾更改文件,若文件不存在则创建文件

需要注意的是上面有b的是针对与二进制文件的,没有b的仅仅只能操作文本文件。

既然要访问文件,那么就需要对文件的路径需要了解。文件路径分为相对路径和绝对路径。

  • 相对路径:从磁盘的根盘符开始,找到待访问的文件路径。需要注意的是在Windows中使用\的话需要使用\\才行。而在Linux中不支持这种分割方法。还有一种使用/分割文件,这种方法不仅在Windows中可以用,在Linux中也同样适用。
  • 绝对路径:相对路径如果使用VS的话有两种情况。一种是VS的环境下编译执行,则文件相对路径是相对于项目名.vcxproj所在的目录位置。如果是直接运行可执行文件,则是相对于可执行文件所在的目录位置。

上面叙述了那么多,接下来看看上面的代码是如何使用的。

首先给出一个fputc的例子,将二十六个字母写入到指定文件中。

#if 0

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <Windows.h>

// fputc函数的使用
void test1()
{
	char *filename = "test03-1.txt";

	FILE *fp = fopen(filename, "w");
	if (fp == NULL)
	{
		perror("fopen error");
		return -1;
	}

	int ret = fputc('A', fp);

	printf("ret = %d\n", ret);
	if (ret == -1)
	{
		perror("fputc error");
		return -1;
	}

	fclose(fp);
}


// 在文件中写入二十六个小写字母
void test2()
{
	char *filename = "test03-2.txt";

	FILE *fp = fopen(filename, "w");
	if (fp == NULL)
	{
		perror("fopen error");
		return -1;
	}

	int ret = 0;

	for (int i = 0; i < 26; i++)
	{
		ret = fputc('a' + i, fp);

		if (ret == -1)
		{
			perror("fputc error");
			return -1;
		}

	}

	fclose(fp);

}

int main(int argc, char* argv[])
{
	//test1();
	test2();
	printf("=======finish=========\n");

	system("pause");
	return 0;
}

#endif

使用fgetc读取文件的示例如下:

#if 0

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <Windows.h>

int main(int argc, char* argv[])
{
	srand((unsigned int)time(NULL));

	char *filename = "test04-1.txt";
	FILE *fp = fopen(filename, "r");

	// 读失败就写
	if (fp == NULL)
	{
		perror("fopen error");
		fp = fopen(filename, "w");

		// 写失败就关闭
		if (fp == NULL)
		{
			perror("fopen error");
			return -1;
		}

		int ret = 0;
		for (int i = 0; i < 10; i++)
		{
			ret = fputc(rand() % 26 + 'a', fp);
			if (ret == -1)
			{
				perror("fputc error");
				if (fp) fclose(fp);
				return -1;
			}
		}
		
		fclose(fp);

		fp = fopen(filename, "r");
		if (fp == NULL)
		{
			perror("fopen error");
			return -1;
		}
	}

	int ret = 0;
	for (int i = 0; i < 11; i++)
	{
		ret = fgetc(fp);
		printf("%d\n", ret);
	}

	fclose(fp);

	system("pause");
	return 0;
}

#endif

使用feof的例子如下:

#if 0

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <Windows.h>

// 向文件中写入数据
void test1(char *filename)
{
	FILE *fp = fopen(filename, "w");
	if (!fp)
	{
		perror("fopen error");
		return;
	}
	
	fputc('a', fp);
	fputc('f', fp);
	fputc(-1, fp);
	fputc('s', fp);
	fputc('v', fp);
	fputc('g', fp);
	fputc('h', fp);

	fclose(fp);
}

// 读文件,使用EOF判断文件末尾,只能判断文本文件
void test2(char *filename)
{
	FILE *fp = fopen(filename, "r");
	if (!fp)
	{
		perror("fopen error");
		return;
	}

	while (1)
	{
		char ch = fgetc(fp);
		if (ch == EOF) return;
		printf("%d \n", ch);
	}

	fclose(fp);
}

// 使用feof判断文件结束
void test3(char *filename)
{
	FILE *fp = fopen(filename, "r");
	if (!fp)
	{
		perror("fopen error");
		return;
	}

	while (1)
	{
		char ch = fgetc(fp);
		if (feof(fp))
		{
			break;
		}
		printf("%c\n", ch);
	}

	fclose(fp);
}

int main(int argc, char* argv[])
{
	char *filename = "test05-1.txt";

	//test1(filename);
	//test2(filename);
	test3(filename);

	system("pause");
	return 0;
}

#endif

feof函数有个特性,要是用它检测文件结束标志,必须在调用该函数之前,使用读文件函数。除此之外判断文件结束的标志也可以使用EOF。EOF是宏定义的-1。但是EOF与feof函数的区别是EOF只能判断文本文件的结尾,非文本文件就无法进行判断。

接下来看一个关于fgets和fputs函数的示例代码:

#if 0

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <Windows.h>


int main(int argc, char* argv[])
{
	char *filename = "test06-1.txt";
	FILE *fp = fopen(filename, "w");
	if (fp == NULL)
	{
		perror("fopen error");
		return -1;
	}

	char buf[4096] = { 0 };

	while (1)
	{
		fgets(buf, 4096, stdin);
		if (strcmp(buf, ":wq\n") == 0)
		{
			break;
		}
		fputs(buf, fp);
	}

	fclose(fp);

	system("pause");
	return 0;
}

#endif

3. 文件版四则运算

在一个文件中写入一系列待计算的运算式,并使用文件的读写操作将运算的结果该入改文件中。

思路:首先封装write_file函数,将四则运算的表达式写入。其次封装read_file函数,将四则运算的表达式读出,使用sscanf进行拆分,然后在封装一个calc函数对拆分出来的表达式进行计算。然后将最后的运算式再用sprintf拼接回去。最后将所有的结果的运算式拼接起来。将原文件关闭再重新用写的方式打开,就清空了原文件的内容,此时再将结果写入。

#if 1

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <Windows.h>

#define N 20

// 四则运算写入
void write_file(char *filename)
{
	srand((unsigned int)time(NULL));

	char buf[4096] = { 0 };
	FILE *fp = fopen(filename, "w");
	if (!fp)
	{
		perror("fopen error");
		return;
	}

	for (int i = 0; i < N; i++)
	{
		int number1 = rand() % 1000 + 1;
		char ch = rand() % 4;
		int number2 = rand() % 1000 + 1;

		switch (ch)
		{
		case 0:
			ch = '+';
			break;
		case 1:
			ch = '-';
			break;
		case 2:
			ch = '*';
			break;
		case 3:
			ch = '/';
			break;
		}

		sprintf(buf, "%d%c%d=\n", number1, ch, number2);
		fputs(buf, fp);
		fputs(buf, stdout);
	}

	fclose(fp);
}

int calc(char ch, int number1, int number2)
{
	switch (ch)
	{
	case '+':
		return number1 + number2;
	case '-':
		return number1 - number2;
	case '*':
		return number1 * number2;
	case '/':
		return number1 / number2;
	default:
		break;
	}
}

void read_files(char *filename)
{
	
	char res_buf[4096] = { 0 };
	char buf[4096] = { 0 };

	FILE *fp = fopen(filename, "r");
	if (fp == NULL)
	{
		perror("fopen error");
		return;
	}

	while (1)
	{
		fgets(buf, 4096, fp);
		
		if (feof(fp))
		{
			break;
		}

		int number1 = 0;
		int number2 = 0;
		char op = 0;
		sscanf(buf, "%d%c%d=\n", &number1, &op, &number2);

		int op_res = calc(op, number1, number2);

		sprintf(buf, "%d%c%d=%d\n", number1, op, number2, op_res);
		strcat(res_buf, buf);
	}

	fclose(fp);

	fp = fopen(filename, "w");
	if (!fp)
	{
		perror("fopen error");
		return;
	}

	fputs(res_buf, fp);

	fclose(fp);

}

int main(int argc, char* argv[])
{
	char *filename = "test07-1.txt";

	write_file(filename);
	printf("==========================\n");

	read_files(filename);

	system("pause");
	return 0;
}

#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值