C入门系列:第六章 文件输入/输出

C入门系列:第一章 基本类型

C入门系列:第二章 字符串

C入门系列:第三章 函数

C入门系列:第四章 数组和指针

C入门系列:第五章 数据存储类别和内存管理

C入门系列:第六章 文件输入/输出

C入门系列:第七章 结构和其他数据形式

C入门系列:第八章 C预处理器和C库

1 文件

fopen() 第1个参数为文件名,第二个参数为创建的文件开启的权限,返回 FILE* 指针,一般与 fclose() 配合使用,fclose()传入fopen()返回的文件指针。

fopen()返回的文件指针并不指向实际的文件,它指向一个包含文件信息的数据对象,其中包含操作文件的I/O函数所用的缓冲区信息。

fclose()返回整数值,返回0表示关闭成功,否则返回EOF失败。

在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>

// argc表示输入参数
// argv表示参数数据,argv[0]为终端输入的命令
int main(int argc, char *argv[]) {
	int ch;
	FILE *fp; // 文件指针
	unsigned long count = 0;
	if (argc != 2) {
		printf("Usage:%s filename\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	// fopen()创建一个文件
	if ((fp = fopen(argv[1], "r")) == NULL) {
		printf("Can't open %s\n", argv[1]);
		exit(EXIT_FAILURE);
	}
	
	// 将输入写入文件,读取到文件结束符EOF=-1则结束
	while ((ch = getc(fp)) != EOF) {
		putc(ch, stdout);
		count++;
	}
	
	// 关闭文件释放内存
	fclose(fp); 

	return 0;
}

2 getc()和putc()

getc()putc() 函数与 getchar()putchar() 函数类似。所不同的是getc()和putc()要告诉函数使用哪一个文件。

char ch;
FILE *fp = fopen("test.txt", "r");
// 获取标注输入字符
ch = getchar();
ch = getc(fp);
putc(ch, fpout);
fclose(fp);

3 文件拷贝压缩

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define LEN 40
int main(int argc, char *argv[]) {
	FILE *in, *out;
	int ch;
	char name[LEN];
	int count = 0;
	
	// 检查终端输入参数
	if (argc < 2) {
		fprintf(stderr, "Usage:%s filename\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	if ((in = fopen(argv[1], "r")) == NULL) {
		fprintf(stderr, "I couldn't open the file");
		exit(EXIT_FAILURE);
	}
	strncpy(name, argv[1], LEN - 5); // 拷贝文件名
	name[LEN - 5] = '\0'; // 设置空字符
	strcat(name, ".red"); // 拼接字符串,文件名后添加.red
	if ((out = fopen(name, "w)) == NULL) {
		fprintf(stderr, "Can't create output file");
		exit(3);
	}

	// 拷贝数据
	while ((ch = getc(in)) != EOF) {
		if (count++ % 3 == 0) {
			putc(ch, out); // 打印3个字符中的第1个字符
		}
	}

	// 关闭文件
	if (fclose(in) != 0 || fclose(out) != 0)
		fprintf(stderr, "Error in cloing files");

	return 0;
}

4 fprintf()和fscanf()

fprintf()scanf() 函数与 printf()sanf() 类似,区别在于前者需要用第1个参数指定待处理的文件。

rewind() 函数传入一个文件指针,将文件光标返回到开始位置,方便从头开始读取文件。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 41

int main(void) {
	FILE *fp;
	char words[MAX];

	if ((fp = fopen("wordy", "a+") == NULL) {
		fprintf(stdout, "Can't open file");
		exit(EXIT_FAILURE);
	}

	puts("Enter words to add to the file; press the #");
	puts("key at the beginning of a line to terminate");	
	
	// 将终端输入写入文件,输入'#'结束
	while ((fscanf(stdin, "%40s", words) == 1) && (words[0] != '#')) {
		fprintf(fp, "%s", words); 
	}
	
	puts("File contents:");
	rewind(fp); // 返回文件开始处
	while (fscanf(fp, "%s", words) == 1) {
		puts(words);
	}

	if (fclose(fp) != 0) {
		fprintf(stderr, "Error closing file");
	}

	return 0;
}

5 fgets()和fputs()

fgets(buf, STLEN, fp);

buf 是char类型数组的名称,STLEN 是字符串的大小,fp 文件指针。

fgets() 函数读取输入直到第1个换行符的后面或读到文件结尾,或者读取STLEN-1个字符。fgets()在末尾添加一个空字符使之成为一个字符串,字符串的大小是其字符加上一个空字符。

如果fgets()在读到字符上限之前已读完一整行,它会把表示行结尾的换行符放在空字符前面。

fgets()函数在遇到EOF时将返回NULL,可以利用这一机制检查是否到达文件结尾;如果未遇到EOF则返回传给它的地址。

fputs(buf, fp);

buf 是字符串的地址,fp 用于指定目标文件。

由于fgets()保留了换行符,fputs()就不会再添加换行符。

6 fseek()和ftell()

有了 fseek() 函数,便可以把文件看作是数组,在fopen()打开的文件中直接移动到任意字节处。fseek()返回int类型的值。

ftell() 函数返回一个long类型的值,表示文件中的当前位置。

// 倒叙显示文件的内容
#include<stdio.h>
#include<stdlib.h>
#define CNTL_Z '\032'
#define SLEN 81
int main(void) {
	char file[SLEN];
	char ch;
	FILE *fp;
	long count, last;

	puts("Enter the name of the file to be proccessed");
	scanf("%80s", file);
	if ((fp = fopen(file, "rb")) == NULL) {
		printf("reverse can't open file");
		exit(EXIT_FAILURE);
	}

	// 第1个参数:FILE*文件指针
	// 第2个参数:偏移量,表示从起始点要移动的距离,必须是long类型,可以为正(前移)、负(后移)或0(保持不动)
	// 第3个参数:模式,有三种模式:
	// 1、SEEK_SET:文件开始处
	// 2、SEEK_CUR:当前位置
	// 3、SEEK_END:文件末尾
	fseek(fp, 0L, SEEK_END); // 定位到文件末尾

	last = ftell(fp); // 把从文件开始处到文件结尾的字节数赋给last

	for (count = 1L; count <= last, count++) {
		fseek(fp, -count, SEEK_END); // 回退
		ch = getc(fp);
		// MS-DOS文件
		if (ch != CNTL_Z && ch != '\r') {
			putchar(ch);
		}
	}
	putchar('\n');
	
	fclose(fp);
	
	return 0;
}

7 fgetpos()和fsetpos()

fseek()ftell() 潜在问题是,它们都把文件大小限制在long类型能表示的范围内。ANSI C新增了两个处理较大文件的新定位函数:fgetpos()fsetpos()

这两个函数不使用long类型的值表示位置,使用一种新类型:fpos_t类型。fpos_t类型的变量或数据对象可以在文件中指定一个位置,它不能是数组类型。

// 调用该函数时,fpos_t类型的值放在pos指向的位置上,该值描述了文件中的一个位置。如果成功,fgetpos()函数返回0;如果失败,返回非0
int fgetpos(FILE* restrict stream, fpos_t * restrict pos);

// 调用该函数时,使用pos指向位置上的fpos_t类型值来设置文件指针指向该值指定的位置。如果成功,fsetpos()函数返回0,否则返回非0
int fsetpos(FILE *stream, const fpos_t *pos);

8 ungetc()

int ungetc(int c, FILE *fp) 函数把c指定的字符放回输入流中。如果把一个字符放回输入流,下次调用标准输入函数时将读取该字符。

9 fflush()

int fflush(FILE *fp);

调用 fflush() 函数引起输出缓冲区中所有的未写入数据被发送到 fp 指定的输出文件。如果 fp 是空指针,所有输出缓冲区都被刷新。在输入流中使用fflush()函数的效果是未定义的。只要最近一次操作不是输入操作,就可以用该函数来更新流。

10 setvbuf()

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

setvbuf() 函数创建了一个供标准I/O函数替换使用的缓冲区。在打开文件后且未对流进行其他操作之前,调用该函数。

指针 fp 识别待处理的流;

buf 指向待使用的存储区,如果buf的值不是NULL,则必须创建一个缓冲区;

mode 设置缓冲区的模式:

  • _IOFBF表示完全缓冲(在缓冲区满时刷新)

  • _IOLBF表示行缓冲(在缓冲区满时或写入一个换行符时)

  • _IONBF表示无缓冲

如果操作成功,函数返回0,否则返回一个非0值。

11 fread()和fwrite()

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

fwrite() 函数把二进制数据写入文件。

指针 ptr 是待写入数据块的地址,size 表示待写入数据块的大小(以字节为单位),nmemb 表示待写入数据块的数量。

// 把一块256字节的数据从buffer写入文件
char buffer[256];
fwrite(buffer, 256, 1, fp); 

// 把earnings数组中的数据写入文件
double earnings[10];
fwrite(earnings, sizeof(double), 10, fp);

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

指针ptr表示待读取文件数据在内存中的地址。

// 从earnings数组读取数据,如果读取正常则返回值就是nmemb
// 如果读取错误或到文件结尾,返回值比nmemb小
double earnings[10];
fread(earnings, sizeof(double), 10, fp);

12 feof()和ferror()

如果标准输入函数返回EOF,则通常表明函数已到达文件结尾;然而,出现读取错误时,函数也会返回EOF。

feof()ferror() 用于区分这两种情况。

当上一次输入调用检测到文件结尾时,feof()返回一个非0值,否则返回0。
当读或写出现错误,ferror()返回一个非0值,否则返回0。

// 把文件附加到另一个文件末尾
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define BUFSIZE 4096
#define SLEN 81
void append(FILE *source, FILE *dest);
char * s_gets(char * st, int n);
int main(void) {
	FILE *fa, *fs; // fa指向目标文件,fs指向源文件
	int files = 0; // 附加的文件数量
	char file_app{SLEN]; // 目标文件名
	int ch;

	puts("Enter name of destination file");
	s_gets(file_app, SLEN);
	if ((fa = fopen(file_app, "a+")) == NULL) {
		fprintf(stderr, "Can't open file %s", file_app);
		exit(EXIT_FAILURE);
	}
	
	// 创建输出缓冲区
	if (setvbuf(fa, NULL, _IOFBF, BUFSIZE) != 0) {
		fputs("Can't create output buffer");
		exit(EXIT_FAIULRE);
	}
	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't append file to itself");
		} else if ((fs = fopen(file_src, "r")) == NULL) {
			fpritnf(stderr, "Can't open %s", file_src);
		} else {
			// 创建输入缓冲区
			if (setvbuf(fs, NULL, _IOFBF, BUFSIZE) != 0) {
				fputs("Can't create input buffer");
				continue;
			}
			// 拼接文件内容
			append(fs, fa);
			// 检查是否读取正常
			if (ferror(fs) != 0) {
				fprintf(stderr, "Error in reading file %s", file_src);
			}
			if (ferror(fa) != 0) {
				fprintf(stderr, "Error in writing file %s", file_app);
			}
			fclose(fs);
			files++;
			printf("File %s appended", file_src);
		}
	}

	
	printf("Done appending. %d files appended", files);
	rewind(fa);
	while ((ch = getc(fa)) != EOF) {				
			putchar(ch);
	}
	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);
	}
}

char * s_gets(char *st, int n) {
	char * ret_val;
	char * find;

	ret_val = fgets(st, n, stdin);
	if (ret_val) {
		find = strchr(st, '\n');// 查找换行符
		if (find)				// 如果地址不是NULL
			*find = '\0';		// 放置一个空字符
		else 
			while (getchar() != '\n')
				continue;
	}
	return ret_val;
}

// 二进制I/O进行随机访问
#include<stdio.h>
#include<stdlib.h>
#define ARSIZE 1000
int main() {
	double numbers[ARSIZE];
	double value;
	const char * file = "numbers.dat";
	int i;
	long pos;
	FILE *iofile;

	// 创建一组double类型的值
	for (i = 0; i < ARSIZE; i++) {
		numbers[i] = 100.0 * i + 1.0 / (i + 1);
	}

	if ((iofile = fopen(file, "wb")) == NULL) {
		exit(EXIT_FAILURE);
	}

	// 以二进制格式把数组写入文件
	fwrite(numbers, sizeof(double), ARSIZE, iofile);
	fclose(iofile);
	
	if ((iofile = fopen(file, "rb")) == NULL) {
		exit(EXIT_FAIULRE);
	}
	// 从文件中读取选定的内容
	while (scanf("%d", &i) == 1 && i >= 0 && i < ARSIZE) {
		pos = (long) i * sizeof(double); // 计算偏移量
		fseek(iofile, pos, SEEK_SET); 	 // 定位到此处
		fread(&value, sizeof(double), 1, iofile);
	}
	fclose(iofile);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值