22、一文带你掌握C语言文件操作-创新互联

一文带你掌握C语言文件操作
  • 文件操作
    • 1 文件相关
      • 1.1 文件名
      • 1.2 文件类型
      • 1.3 文件缓冲区
      • 1.4 文件指针
    • 2 文件的打开和关闭
    • 3 文件的顺序读写
      • 3.1 字符输入输出
      • 3.2 文本输入输出
      • 3.3 读取有格式的文件
      • 3.4 二进制输入输出
    • 4 文件的随机读写
      • `1、fseek`
      • `2、ftell`
      • `3、rewind`
    • 5 文件结束判定
      • 被错误使用的feof

创新互联建站是一家以成都网站建设、网页设计、品牌设计、软件运维、成都网站营销、小程序App开发等移动开发为一体互联网公司。已累计为成都汽车玻璃修复等众行业中小客户提供优质的互联网建站和软件开发服务。文件操作 1 文件相关 1.1 文件名

一个文件要有一个唯一的标识,以便用户识别和引用。

文件名包含3个部分:文件路径 + 文件名主干 + 文件后缀

c:\code\test.txt

1.2 文件类型

根据数据的组织形式,对于数据文件来说,一般分为二进制文件和文本文件

  • 二进制文件

    数据在内存中以二进制的形式存储

  • 文本文件

    要求在外存上以ASCII码的形式存储,则需要在存储前转换。

1.3 文件缓冲区

系统会自动的在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”

从内存向磁盘输出数据,会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。

从磁盘向计算机读入数据,则从磁盘文件中读取数据到内存缓冲区,然后再从缓冲区逐个将数据送到程序数据区(程序变量等)

缓冲区的大小根据编译系统确定

1.4 文件指针

文件类型指针:简称文件指针

每个被使用的文件,都在内存中开辟了一个相应的文件信息区,用来存放文件的相关(如文件名,文件状态,文件当前位置等),这些信息保存在一个结构体变量中。

该结构体类型是有系统声明的,取名FILE

typdef struct _iobuf FILE

每当打开一个文件时,系统会根据文件的情况,自动创建一个FILE类型的变量,并填充里面的信息,使用者不必关注细节。

一般都是通过一个FILE类型的指针,来维护这个FILE结构的变量,这样使用起来会很方便。

创建一个FILE*的指针变量

FILE* pf; // 文件指针变量

也就是说,通过文件指针,能够找到与它相关联的文件。

2 文件的打开和关闭
  • 文件在读写之前应该先打开文件
  • 在使用结束之后应该关闭文件

在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系

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

int flcose(FILE* stream);

文件的打开方式有下面几种

文件使用方式含义如果指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)为了输出数据,打开一个文本建立一个新文件
“a”(追加)向文本文件尾添加数据出错
“rb”(只读)为了输入数据,打开一个二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件建立一个新文件
“ab”(追加)向一个二进制文件尾添加数据出错
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,建立一个新的文件建立一个新文件
“a+”(读写)打开一个文件,在文件尾进行读写建立一个新文件
“rb+”(读写)为了读和写打开一个二进制文件出错
“rw+”(读写)为了读和写,新建一个二进制文件建立一个新文件
“ab+”(读写)打开一个二进制文件,在文件尾进行读写建立一个新文件

在写文件名的时候,可以是相对路径,也可以是绝对路径

注意点

  • w如果存在文件会清空,如果文件不存在,就建立一个新文件。
3 文件的顺序读写
功能函数名适用于
字符输入函数fgetc所有输入流
字符输出函数fputc所有输出流
文本行输入函数fgets所有输入流
文本上输出函数fputs所有输出流
格式化输入函数fscanf所有输入流
格式化输出函数fprintf所有输出流
二进制输入fread文件
二进制输出fwrite文件
3.1 字符输入输出

读文件(字符输入)*

int main()
{FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{// 打开失败
		printf("%s\n",strerror(errno));
		return 0;
	}

	// 打开成功

	// 读文件
	printf("%c ", fgetc(pf));
	printf("%c ", fgetc(pf));
	printf("%c ", fgetc(pf));
	printf("%c ", fgetc(pf));
	printf("%c ", fgetc(pf));

	// 关闭文件
	fclose(pf);
	pf = NULL; // 相当于free一样
}

写文件(字符输出)

int main()
{FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{// 打开失败
		printf("%s\n",strerror(errno));
		return 0;
	}

	// 打开成功

	// 写文件
	fputc('h', pf);
	fputc('e', pf);
	fputc('l', pf);
	fputc('l', pf);
	fputc('o', pf);

	// 关闭文件
	fclose(pf);
	pf = NULL; // 相当于free一样
}

小知识点!!!

键盘和屏幕这两个输入输出流

  • 从键盘输入
  • 输出到屏幕

键盘和屏幕都是外部设备

  • 键盘–标准输入设备
  • 屏幕–标准输出设备

这两个是程序默认打开的两个流设备

分别对应着stdin/stdout/stderr

这三个流的类型都是FILE*

也就是说,键盘其实就是对应着stdin

屏幕对应着stdout

因此我们可以这样设计程序

// 因为 fgetc/foutc 可以处理所有的输入输出流,因此键盘和屏幕也可以处理

在这里插入图片描述

3.2 文本输入输出

char* fgets(char *string, int n, FILE *stream);

string:从stream读的内容,放到string中去。就是一个缓冲的字符数组(字符串)

n:最多读取多少个字符

注意的是 string 在读取之后,本身就有一个换行,这个换行本身就是文件里面的,只不过换行是空白的隐藏起来了。

读文件

int main()
{char buf[1024] = {0};
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{printf("%s\n", strerror(errno));
		return 0;
	}
	// 读文件
	fgets(buf,1024, pf);
	printf("%s", buf);
    // puts(buf);  如果这里使用 puts,puts天生就会在末尾加换行,会出现多个空行的情况
	fgets(buf, 1024, pf);
	printf("%s", buf);

	fclose(pf);
	pf = NULL;
	return 0;
}

写文件

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

int main()
{char buf[1024] = {0};
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{printf("%s\n", strerror(errno));
		return 0;
	}
	// 写文件
	fputs("hello 你好\n", pf);
	fputs("hello world", pf);


	fclose(pf);
	pf = NULL;
	return 0;
}

操作键盘和屏幕读取输出一行

int main()
{char buf[1024];

	fgets(buf,1024,stdin);// 从标准输入流读取
	fputs(buf, stdout);// 输出到标准输出流
	return 0;
}

这种写法相当于

gets(buf); puts(buf);

3.3 读取有格式的文件

int printf(const char* format [,argument]...);// 其实这里默认是针对 stdout的

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

除了比printf多了一个文件流参数之外,其他都一样。

其实printf默认是针对stdout的,也就是说它的流是stdout

fprintf输出函数 写文件

struct S
{int n;
	float score;
	char arr[10];
};
int main()
{struct S s[2] = {{100, 3.14f, "hello" }, {200,8.88f,"world"} };
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{return 0;
	}
	// 格式化写文件
	fprintf(pf, "%d %f %s\n", s[0].n, s[0].score, s[0].arr);
	fprintf(pf, "%d %f %s\n", s[1].n, s[1].score, s[1].arr);

	// 关闭
	fclose(pf);
	pf = NULL;
}

fscanf 输出读文件

struct S
{int n;
	float score;
	char arr[10];
};
int main()
{struct S s[2] = {0};
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{return 0;
	}
	// 格式化读文件 -- 首先要设置好存这个数据的变量 或者 结构体
	// 一定注意到在 scanf时,变量要用其地址来接收
	fscanf(pf, "%d %f %s", &s[0].n, &s[0].score, s[0].arr);
	printf( "%d %f %s", s[0].n, s[0].score, s[0].arr);
	fscanf(pf, "%d %f %s", &s[1].n, &s[1].score, s[1].arr);
	printf("%d %f %s", s[1].n, s[1].score, s[1].arr);




	// 关闭
	fclose(pf);
	pf = NULL;
}

标准输入输出流中读取

struct S
{int n;
	float score;
	char arr[10];
};
int main()
{struct S s[2] = {0};
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{return 0;
	}
	// 格式化读文件 -- 首先要设置好存这个数据的变量 或者 结构体
	// 一定注意到在 scanf时,变量要用其地址来接收
	fscanf(stdin, "%d %f %s", &s[0].n, &s[0].score, s[0].arr);
	
	fprintf(stdout, "%d %f %s", s[0].n, s[0].score, s[0].arr);



	// 关闭
	fclose(pf);
	pf = NULL;
}

scanf/printf是针对标准输入输出流的格式化输入输出语句

fscanf/fprintf是针对所有输入输出流的格式化输入输出语句

sscanf/sprintf 是针对字符串的格式化输入输出,从字符串中读,输出到字符串中去

int main()
{struct S s = {100, 3.14f, "hello"};
    struct S tmp = {0};
    char buf[1024] = {0};
    
    // 把格式化的数据转换成字符串,存储到buf
    sprintf(buf, "%d %f %s", s.n, s.score, s.arr);
    
    // 从buf中读取格式化的数据到tmp中
    sscanf(buf, "%d %f %s", &(tmp.n), &(tmp.float), tmp.arr);
}
3.4 二进制输入输出

把结构体学生的信息存入到文件中去,怎么写呢?

size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);

buffer: 要写的内容

size:元素的大小

count:元素的个数

struct S
{char name[20];
	int age;
	double score;
};
int main()
{struct S s = {"张三", 20, 64.5};
	FILE* pf = fopen("test.txt", "wb");
	if (pf == NULL)
	{return 0;
	}
	
	// 二进制写文件
	fwrite(&s, sizeof(struct S), 1, pf);



	// 关闭
	fclose(pf);
	pf = NULL;
}

fread 读二进制文件

size_t fread(const void* buffer, size_t size, size_t count, FILE* stream);

int main()
{//struct S s = {"张三", 20, 64.5};
	struct S tmp = {0};
	FILE* pf = fopen("test.txt", "rb");
	if (pf == NULL)
	{return 0;
	}
	
	// 二进制写文件
	//fwrite(&s, sizeof(struct S), 1, pf);

	// 二进制读文件
	fread(&tmp, sizeof(struct S), 1, pf); // 二进制读的文件,直接读给这么大的内存空间
	printf("%s %d %f\n", tmp.name, tmp.age, tmp.score);
	// 关闭
	fclose(pf);
	pf = NULL;
}
4 文件的随机读写1、fseek

根据文件指针的位置和偏移量来定位文件指针

int fseek(FILE * stream, long int offset, int origin);

offset:偏移量。单位是字节

  • 正值代表向后偏移
  • 负值代表向前偏移

origin:文件指针的当前位置

  • SEEK_CUR:文件指针当前位置
  • SEEK_END:文件末尾(在最后一个字符的后面,而不是最后一个字符)
  • SEEK_SET:文件起始位置

通过调整文件指针,以当前位置为起点,根据偏移量来调整。

int main()
{	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{return 0;
	}
	
	// 定位文件指针
	// 我们默认的 test.txt 里面存放的数据是 abcdef
	fseek(pf, 2, SEEK_CUR); // 从当前位置向后偏移 2 字节,就是从 c 开始
	//fseek(pf, 2, SEEK_CUR);  从末尾向前偏移 2 字节,e

	// 读取文件
	int ch = fgetc(pf);
	printf("%c\n", ch);  // c



	// 关闭
	fclose(pf);
	pf = NULL;
}
2、ftell

返回文件指针相对于起始位置的偏移量

long int ftell(FILE* stream);

int main()
{	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{return 0;
	}
	
	// 定位文件指针
	// 我们默认的 test.txt 里面存放的数据是 abcdef
	fseek(pf, -2, SEEK_END);
	int pos = ftell(pf); 
	printf("%d\n", pos); // 4

	// 读取文件
	


	// 关闭
	fclose(pf);
	pf = NULL;
}
3、rewind

让文件指针的位置回到文件的起始位置

void rewind(FILE* stream);

int ch = fgetc(pf);
printf("%c\n", ch);

rewind(pf);
printf("%c\n", ch); // 这里依然是a
5 文件结束判定 被错误使用的feof

在文件读取过程中,不能用feof函数的返回值直接用来判断文件是否结束

这个是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束

  • 文本文件读取是否结束,判断返回值是否为EOF或者NULL
    • fgetc判断是否为EOF
    • fgets判断是否为NULL
  • 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数
    • fread判断返回值是否小于实际要读的个数
while((ch = fgetc(pf)) != EOF)
{putchar(ch);
}

if (ferror(pf)) // 判断是否遇到错误而退出
{printf("error\n");
}else if(feof(of)) // 判断是否遇到 EOF而退出
{printf("end of file \n");
}

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


网站栏目:22、一文带你掌握C语言文件操作-创新互联
网页网址:http://pwwzsj.com/article/eedoh.html