C语言——文件操作

一、为什么使用文件

如果没有⽂件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失
了,等再次运⾏程序,是看不到上次程序的数据的,如果要将数据进⾏持久化的保存,我们可以使⽤⽂件

二、什么是文件

磁盘(硬盘)上的⽂件是⽂件。
但是在程序设计中,我们⼀般谈的⽂件有两种: 程序⽂件、数据⽂件(从⽂件功能的⻆度来分类的)

1.程序⽂件

程序⽂件包括源程序⽂件(后缀为.c),⽬标⽂件(windows环境后缀为.obj),可执⾏程序(windows
环境后缀为.exe)

2.数据⽂件

⽂件的内容不⼀定是程序,⽽是程序运⾏时读写的数据,⽐如程序运⾏需要从中读取数据的⽂件,或
者输出内容的⽂件。这里讨论的是数据⽂件。
之前所处理数据的输⼊输出都是以终端为对象的,即从终端的键盘输⼊数据,运⾏结果显⽰到
显⽰器上。
其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使⽤,这⾥处
理的就是磁盘上⽂件

3.⽂件名

⼀个⽂件要有⼀个唯⼀的⽂件标识,以便⽤户识别和引⽤。
⽂件名包含3部分:⽂件路径+⽂件名主⼲+⽂件后缀
例如: c:\code\test.txt
为了⽅便起⻅,⽂件标识常被称为⽂件名。
注意:不是任意字符都可以当文件名
文件名可以不包含后缀名

在这里插入图片描述

三、⼆进制⽂件和⽂本⽂件

根据数据的组织形式,数据⽂件被称为⽂本⽂件或者⼆进制⽂件。

数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存的⽂件中,就是 ⼆进制⽂件
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的⽂件就是 ⽂本⽂件
⼀个数据在⽂件中是怎么存储的呢?
字符⼀律以ASCII形式存储,数值型数据既可以⽤ASCII形式存储,也可以使⽤⼆进制形式存储。

如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占⽤5个字节(每个字符⼀个字节),⽽
⼆进制形式输出,则在磁盘上只占4个字节

在这里插入图片描述

#include <stdio.h>
int main()
{
	int n = 10000;
	FILE* p = fopen("haha.txt", "w");//以二进制的形式写到文件中
	fwrite(&n, 4, 1, p);
	fclose(p);
	p = NULL;
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、文件的打开和关闭

1.流和标准流

我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出
操作各不相同,为了⽅便程序员对各种设备进⾏⽅便的操作,我们抽象出了流的概念,我们可以把流
想象成流淌着字符的河。

C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。
⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。

标准流

那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语⾔程序在启动的时候,默认打开了3个流:
• stdin-标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据
• stdout-标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出
流中。
• stderr-标准错误流,⼤多数环境中输出到显⽰器界⾯。

这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的
stdin、stdout、stderr三个流的类型是: FILE * ,通常称为⽂件指针

C语⾔中,就是通过 FILE * 的⽂件指针来维护流的各种操作的

2.文件指针

要对文件进行操作,离不开⽂件类型指针,简称⽂件指针

每个被使用的文件,都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如:文件名、文件状态、文件位置等),这些信息被保存到一个结构体中,系统为其声明为FILE,每当打开一个文件的时候,系统就会根据情况自动创建一个FILE结构的变量,⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使⽤起来更加⽅便。

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

定义pf是⼀个指向FILE类型数据的指针变量。可以使pf指向某个⽂件的⽂件信息区(是⼀个结构体变
量)。通过该⽂件信息区中的信息就能够访问该⽂件。也就是说,通过⽂件指针变量能够间接找到与
它关联的⽂件

在这里插入图片描述

3.文件的打开和关闭

⽂件在读写之前应该先打开⽂件,在使⽤结束之后应该关闭⽂件。

在编写程序的时候,在打开⽂件的同时,都会返回⼀个FILE*的指针变量指向该⽂件,也相当于建⽴了
指针和⽂件的关系。
ANSIC规定使⽤ fopen 函数来打开⽂件, fclose 来关闭⽂件

文件的打开——fopen( )

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

文件的关闭——fclose( )

int fclose ( FILE * stream );

mode表⽰⽂件的打开模式,下⾯都是⽂件的打开模式:
在这里插入图片描述
举个例子:

#include <stdio.h>
int main()
{
	FILE* pf = fopen("haha.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		fclose(pf);
		pf = NULL;
		return 1;
	}
	fputs("Hi mihayo", pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述
当使用文件操作打开文件时,应该检查文件指针是否正常打开或者打开失败,在使用文件指针前应该判断是否为空指针。
如果为空,应该停止使用该指针并且关闭该文件。
如果为真,则对其进行操作后,关闭该文件,并且将该文件指针置为空指针。

这是良好的编程习惯。

五、文件的顺序读写

在这里插入图片描述

1.fgetc

函数原型:

int fgetc ( FILE * stream );

fgetc(): 从流中获取字符
返回指定流的内部文件位置指示器当前指向的字符。然后,内部文件位置指示器将前进到下一个字符。
返回值
读取成功后返回读取字符的个数
如果位置指示器位于文件末尾,则函数返回 EOF 并设置流的 eof 指示符 (feof)。
如果发生其他读取错误,该函数也会返回 EOF,但会设置其错误指示符 (ferror)。

#include <stdio.h>
int main()
{
	FILE* p = fopen("haha.txt", "r");
	if (p == NULL)
	{
		perror("fopen");
		fclose(p);
		p = NULL;
		return 1;
	}
	int ch = 0;
	//打印文件里面所有的字符串
	while ((ch = fgetc(p)) != EOF)
	{
		printf("%c", ch);
	}
	fclose(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

2.fputc

函数原型:

int fputc ( int character, FILE * stream );

fputc( ): 将字符写入流
将字符写入流并推进位置指示器。
字符写入流的内部位置指示器指示的位置,然后自动前进 1。
返回值
成功后,将返回所写字符。
如果发生写入错误,则返回 EOF 并设置错误指示器(ferror)。

#include <stdio.h>
int main()
{
	FILE* p = fopen("haha.txt", "w");
	if (p == NULL)
	{
		perror("fopen");
		fclose(p);
		p = NULL;
		return 1;
	}
	char ch = 0;
	for (ch = 'a'; ch <= 'd'; ch++)
	{
		fputc(ch, p);
	}
	fclose(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

3.fgets

函数原型:

char * fgets ( char * str, int num, FILE * stream );

fgets():从流中获取字符串
从流中读取字符并将它们作为 C 字符串存储到 str 中,直到读取 (num-1) 个字符或到达换行符或文件末尾,以先发生者为准。
换行符使 fgets 停止读取,但它被函数视为有效字符,并包含在复制到 str 的字符串中。
终止 null 字符会自动附加到复制到 str 的字符之后。
返回值
成功后,该函数返回 str。
如果在尝试读取字符时遇到文件末尾,则设置 eof 指示符 (feof)。如果在读取任何字符之前发生这种情况,则返回的指针为空指针(并且 str 的内容保持不变)。
如果发生读取错误,则设置错误指示符 (ferror),并返回空指针(但 str 指向的内容可能已更改)。

#include <stdio.h>
int main()
{
	FILE* p = fopen("haha.txt", "r");
	if (p == NULL)
	{
		perror("fopen");
		fclose(p);
		p = NULL;
		return 1;
	}
	char arr[60] = { 0 };
	fgets(arr, 7, p);
	printf(arr);
	fclose(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

4.fputs

函数原型:

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

fputs():将字符串写入流

将 str 指向的 C 字符串写入流。
该函数从指定的地址 (str) 开始复制,直到到达终止 null 字符 (‘\0’)。此终止 null 字符不会复制到流中。
返回值
成功后,将返回一个非负值。
出错时,该函数返回 EOF 并设置错误指示器 (ferror)。

#include <stdio.h>
int main()
{
	FILE* p = fopen("haha.txt", "w");
	if (p == NULL)
	{
		perror("fopen");
		fclose(p);
		p = NULL;
		return 1;
	}
	char arr[60] = "HEllo World!";
	fputs(arr, p);
	fclose(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

5.fscanf

函数原型:

int fscanf ( FILE * stream, const char * format, ... );

fscanf():从流中读取格式化数据
从流中读取数据,并根据参数格式将它们存储到其他参数所指向的位置。
其他参数应指向已分配的对象,该对象由格式字符串中的相应格式说明符指定。

相当于在scanf的基础上多了个文件指针

#include <stdio.h>
int main()
{
	FILE* p = fopen("haha.txt", "r");
	int n = 0;
	double m = 0.0;
	char arr[12] = { 0 };
	if (p == NULL)
	{
		perror("fopen");
		fclose(p);
		p = NULL;
		return 1;
	}
	fscanf(p, "%d %lf %s", &n, &m, arr);
	printf("%d %lf %s\n", n, m, arr);
	return 0;
}

在这里插入图片描述

6.fprintf

函数原型:

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

fprintf():将格式化数据写入流式处理
将按格式指向的 C 字符串写入流。如果 format 包含格式说明符(以 % 开头的子序列),则格式后面的其他参数将被格式化并插入到生成的字符串中,以替换其各自的说明符。
在 format 参数之后,该函数至少需要与 format 指定的相同数量的附加参数。

相当于在printf的基础上多了个文件指针

#include <stdio.h>
int main()
{
	FILE* p = fopen("haha.txt", "w");
	if (p == NULL)
	{
		perror("fopen");
		fclose(p);
		p = NULL;
		return 1;
	}
	int n = 114115;
	double m = 66.6;
	char arr[20] = "mihoyo";
	fprintf(p, "%d %lf %s", n, m, arr);
	return 0;
}

在这里插入图片描述

7.fread

函数原型:

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

fread():从流中读取数据块
从流中读取一个计数元素数组,每个元素的大小为字节大小,并将它们存储在 ptr 指定的内存块中。

#include <stdio.h>
int main()
{
	FILE* p = fopen("haha.txt", "r");
	if (p == NULL)
	{
		perror("fopen");
		fclose(p);
		p = NULL;
		return 1;
	}
	int arr[5] = { 0 };
	int len = sizeof(arr) / sizeof(arr[0]);
	fread(arr, sizeof(int), len, p);
	for (int i = 0; i < len; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

在这里插入图片描述

8.fwrite

函数原型:

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

fwrite():把数据块中的数据写入到流中
将ptr指向的内存中的数据,按照指定的个数写入到流中

#include <stdio.h>
int main()
{
	FILE* p = fopen("haha.txt", "w");
	int arr[5] = { 1,2,3,4,5 };
	if (p == NULL)
	{
		perror("fopen");
		fclose(p);
		p = NULL;
		return 1;
	}
	int len = sizeof(arr) / sizeof(arr[0]);
	fwrite(arr, sizeof(int), len, p);
	fclose(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

六、文件的随机读写

1.fseek

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

根据⽂件指针的位置和偏移量来定位⽂件指针
FILE * stream是文件流,即指向的文件
offset是指针的偏移量
origin是起始位置

在这里插入图片描述

#include <stdio.h>
int main()
{
	FILE* p = fopen("haha.txt", "w");
	if (p == NULL)
	{
		perror("fopen");
		fclose(p);
		p = NULL;
		return 1;
	}
	fputs("Hello mihaaa yi", p);
	fseek(p, 9, SEEK_SET);
	fputs("oyo", p);
	fclose(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

2.ftell

long int ftell ( FILE * stream );

返回⽂件指针相对于起始位置的偏移量
可以和fseek配合使用来获得文件的大小

#include <stdio.h>
int main()
{
	FILE* p = fopen("haha.txt", "r");
	if (p == NULL)
	{
		perror("fopen");
		fclose(p);
		p = NULL;
		return 1;
	}
	fseek(p, 0, SEEK_END);
	int len = ftell(p);
	printf("%d\n", len);
	return 0;
}

在这里插入图片描述

3.rewind

void rewind ( FILE * stream );

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

#include <stdio.h>
int main()
{
	FILE* p = fopen("haha.txt", "r");
	if (p == NULL)
	{
		perror("fopen");
		fclose(p);
		p = NULL;
		return 1;
	}
	fseek(p, 6, SEEK_SET);
	int len = ftell(p);
	printf("%d\n", len);
	rewind(p);
	len = ftell(p);
	printf("%d\n", len);
	return 0;
}

在这里插入图片描述

七、⽂件读取结束的判定

被错误使⽤的 feof
牢记:在⽂件读取过程中,不能⽤feof函数的返回值直接来判断⽂件的是否结束。
feof 的作⽤是:当⽂件读取结束的时候,判断是读取结束的原因是否是:遇到⽂件尾结束。

  1. ⽂本⽂件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
    例如:
    • fgetc 判断是否为 EOF
    • fgets 判断返回值是否为 NULL
  2. ⼆进制⽂件的读取结束判断,判断返回值是否⼩于实际要读的个数

文本文件的判断:

#include<stdio.h>
#include<string.h>
#include<errno.h>

int main()
{
	FILE* pf = fopen("haha.txt", "r");

	if (pf == NULL)
	{
		perror("fopen is failed !");
		fclose(pf);
		pf = NULL;
		return 1;
	}
	int c = 0;
	//EOF本质是-1
	while ((c = fgetc(pf)) != EOF)
	{
		putchar(c);
	}
	if (ferror(pf))
	{
		printf("读取中出现错误\n");
	}
	else if (feof(pf))
	{
		printf("读取到文件尾\n");
	}

	fclose(pf);
	pf = NULL;

	return 0;
}

二进制文件的判断:

#include<stdio.h>
#include<string.h>
#include<errno.h>

int main()
{
	FILE* pf = fopen("haha.txt", "rb");
	int arr[5] = { 0 };
	if (pf == NULL)
	{
		perror("fopen");
		fclose(pf);
		pf = NULL;
		return;
	}
	size_t num = fread(arr, sizeof(int), 5, pf);
	if (num == 5)
	{
		printf("读取成功\n");
	}
	else
	{
		if (ferror(pf))
		{
			printf("读取出现错误\n");
		}
		else if (feof(pf))
		{
			printf("读取到文件尾\n");
		}
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

八、文件缓冲区

ANSIC标准采⽤“缓冲⽂件系统”处理的数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为
程序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓
冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输
⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓
冲区的⼤⼩根据C编译系统决定的

#include <stdio.h>
#include <windows.h>
//VS2019 WIN11环境测试
int main()
{
 FILE*pf = fopen("test.txt", "w");
 fputs("abcdef", pf);//先将代码放在输出缓冲区
 printf("睡眠10秒-已经写数据了,打开test.txt⽂件,发现⽂件没有内容\n");
 Sleep(10000);
 printf("刷新缓冲区\n");
 fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到⽂件(磁盘)
 //注:fflush 在⾼版本的VS上不能使⽤了
 printf("再睡眠10秒-此时,再次打开test.txt⽂件,⽂件有内容了\n");
 Sleep(10000);
 fclose(pf);
 //注:fclose在关闭⽂件的时候,也会刷新缓冲区
 pf = NULL;
 return 0;
}

这⾥可以得出⼀个结论:
因为有缓冲区的存在,C语⾔在操作⽂件的时候,需要做刷新缓冲区或者在⽂件操作结束的时候关闭⽂件。
如果不做,可能导致读写⽂件的问题

  • 29
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值