c学习笔记 文件输入/输出 20210315

标准I/O的机理

第一步:调用fopen()打开文件, fopen()在打开文件的同时创建了一个缓冲区(读写模式下两个)以及一个包含文件和缓冲区的结构, 同时返回一个指向该结构的指针,若把指针赋给一个指针变量fp, 文本模式下打开, 获得一个文本流; 二进制模式则为二进制流。
fopen()创建的结构通常包含一个指定流中当前位置的文件位置指示器, 错误和文件结尾的指示器, 一个指向缓冲区的指针, 一个文件标识符和一个计数器(统计实际拷贝进缓冲区的字节数)。
第二步:文件输出, 调用一个定义在stdio.h中的输入函数。文件中的缓冲数据块被拷贝至缓冲区, 大小一般是512字节或其整数倍。
输入函数发现已读完缓冲区的全部字符后, 会请求把下一个数据块从文件拷贝到该缓冲区。
输出函数同理。

其他I/O函数

int ungetc(int c, FILE *fp)函数

该函数把c指定的字符放回输入流中。

int fflush()函数

int fflush (FILE * fp);

该函数使输出缓冲区所有未写入数据发送到文件fp中, 即刷新缓冲区, 若fp为空指针, 所有输出缓冲区都被刷新。
输入流中该函数的效果未定义。

int setvbuf()函数

int setvbuf(FILE * restrict fp, char * restrict buf, int mode, size_t size);

setvbuf()函数可创建一个供I/O函数替换的缓冲区。
fp识别待处理的流, buf指向待使用的存储区, 若其值不是NULL, 则必须创建一个缓冲区, 如果·NULL是buf的值, 则该函数会自行分配一个缓冲区; size告诉stevbuf()数组的大小·; mode选择如下:
_IOFBF ——————完全缓冲
_IOLBF——————行缓冲
_IONBF——————无缓冲
函数操作成功返回0, 否则返回非0。

二进制I/O: fread() & fwrite()

double 1. / 3.;
fpriintdf(fp, "%f", num);

实际上只是把数值转换为字符串进行存储。如果要保证数值储存前后一致, 则应使用与计算机相同的位。

fwrite(&num, sizeof(double), 1, fp);

fread()和fwrite()用于以二进制形式处理数据
通常使用二进制模式在二进制格式文件中储存二进制数据。

size_t fwrite() 函数

size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb, FILE * restrict fp);

fwrite()将二进制文件写入文件, ptr为待写入数据块的地址, size为数据块的大小, nmemb为待写入数据块的数量, fp指定待写入文件。

double earnings[10];
fwrite(earnings, sizeof(double), 10, fp);

fwrite()函数返回成功写入项的数量, 出错时返回值会比nmemb小。

size_t fread()函数

size_t fread(void * restrict ptr, size_t size, size_t nmemb, FILE * restrict fp);

ptr为待读取文件在内存中的地址, fp指定待读取的文件, 该函数用于读取被fwrite()写入文件的数据。

double earnings[10];
fread(earnings, sizeof (double), 10, fp);

该调用把10个double大小的值拷贝至earnings数组中。
该函数返回成功读取项的数量

int feof(FILE * fp) & int ferror(FILE * fp)函数

用于区分是标准输入函数到达文件结尾返回的eof还是读取错误导致的。
feof()在上一次输入调用检测到文件结尾的话, 返回非零, 否则返回0;
ferror()在上一次输入调用检测到读取错误的话, 返回非零, 否则返回0;

程序示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 4096
#define SLEN 81
void append(FILE* source, FILE* dest);
int main(void)
{
	extern char* s_gets(char* str, int n);
	FILE* fa, * fs;
	int files = 0, ch;
	char file_app[SLEN], file_src[SLEN];

	puts("Enter name of destination file:");
	s_gets(file_app, SLEN);
	if ((fa = fopen(file_app, "a+")) == NULL)
	{
		fprintf(stderr, "Can not open %s\n", file_app);
		exit(EXIT_FAILURE);
	}
	if (setvbuf(fa, NULL, _IOFBF, BUFSIZE) != 0)
	{
		fputs("Can't create output buffer\n", stderr);
		exit(EXIT_FAILURE);
	}
	puts("Enter name of first source file (empty line to quit):");
	while (s_gets(file_src, SLEN) && file_src[0] != '\0')
	{
		if (strcmp(file_src, file_app) == 0)
			fputs("Can not append file to itself\n", stderr);
		else if ((fs = fopen(file_src, "r")) == NULL)
			fprintf(stderr, "Can't open %s\n", file_src);
		else
		{
			if (setvbuf(fs, NULL, _IOFBF, BUFSIZE) != 0)
			{
				fputs("Can't create input buffer\n", stderr);
				continue;
			}
			append(fs, fa);
			if (ferror(fs) != 0)
				fprintf(stderr, "Error in reading file %s.\n", file_src);
			if (ferror(fa) != 0)
				fprintf(stderr, "Error in writing file %s.\n", file_app);
			fclose(fs);
			files++;
			printf("FILE %s appended. \n", file_src);
			puts("Next file (empty line to quit):");
		}
	}
	printf("Done appending. %d files appended.\n", files);
	rewind(fa);
	printf("%s contents:\n", file_app);
	while ((ch = getc(fa)) != EOF)
		putchar(ch);
	puts("Done displaying.");
	fclose(fa);
	return 0;
}

void append(FILE* source, FILE* dest)
{
	size_t bytes;
	static char temp[BUFSIZE];

	while ((bytes = fread(temp, sizeof(char), BUFSIZE, source)) > 0)
		fwrite(temp, sizeof(char), bytes, dest);
}

用二进制I/O进行随机访问

#include <stdio.h>
#include <stdlib.h>
#define ARSIZE 1000 
int main(void)
{
	double numbers[ARSIZE], value;
	const char* file = "number.dat";
	int i;
	long pos;
	FILE* iofile;

	for (i = 0; i < ARSIZE; i++)
		numbers[i] = 100.0 * i + 1.0 / (i + 1);
	if ((iofile = fopen(file, "wb")) == NULL)
	{
		fprintf(stderr, "Could not open %s for output.\n", file);
		exit(EXIT_FAILURE);
	}
	fwrite(numbers, sizeof(double), ARSIZE, iofile);
	fclose(iofile);
	if ((iofile = fopen(file, "rb")) == NULL)
	{
		fprintf(stderr, "Could not open %s for random access.\n", file);
		exit(EXIT_FAILURE);
	}
	printf("Enter an index in the range 0-%d.\n", ARSIZE - 1);
	while (scanf("%d", &i) == 1 && i >= 1 && i < ARSIZE)
	{
		pos = (long)i * sizeof(double);
		fseek(iofile, pos, SEEK_SET);
		fread(&value, sizeof(double), 1, iofile);
		printf("The value there is %f.\n", value);
		printf("Next index (out of range to quit):\n");
	}
	fclose(iofile);
	puts("bye!");
	return 0;
}

程序以二进制模式创建了一个number.dat的文件, 使用fwrite()把数组内容拷贝至文件,储存在文件中的每个值都与储存在内存中的值完全相同, 无任何精细度损失, 每个值在文件中占64位大小, 每个值的位置很好算出。
程序第二部分用于打开待读取文件, 提示用户输入一个值的索引, 把索引值与double类型值占用的字节数相乘, 即可得出文件上的一个位置, 随后调用fseek()定位到该位置, 用fread()进行读取, fread()从已定位位置拷贝8字节到内存中地址为&value的位置。

关键概念

c把输入看作为字节流, 输入流来源为文件, 输入设备, 或者其他程序的输出。
输出也看作是字节流, 目的地可以是文件, 视频显示等等
c对流的解释方法取决于使用的输入输出函数
访问文件必须创建文件指针

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值