首页 > 其他分享 >文件操作(下)

文件操作(下)

时间:2024-10-15 23:46:04浏览次数:7  
标签:文件 ch 读取 int pf FILE 操作

目录

文件的顺序读写

顺序读写函数介绍

在这里插入图片描述

fputc

fputc的详细介绍
函数格式 int fputc ( int character, FILE * stream );

在这里插入图片描述

最开始打开文件时,文件里什么都没有,但是文件的指针是指向这个文件的启始位置(这个指针是一个状态指针,不是pf,具体为什么我也不知道,可能pf只是为了找到这个文件的位置,然后再通过这个状态指针将字符输出)

之后fputc将字符’j’输出进文件,然后文件指针会跳到下一个,然后再分别将字符’a’‘c’'k’输出(注意这里的pf是不变的)

最后再将文件关闭保存
在这里插入图片描述

我们将26个字符输出,注意这里ch已经是字符了,所有不用加’ ’

通过fputc也是可以将字符输出在屏幕上的,因为pf是FILE类型的指针,而在文件操作(上)中,有提到stdout标准输出流,是输出至显示器界面的,并且也是FILE类型的指针
在这里插入图片描述

fgetc

fgetc的详细介绍
函数格式 int fgetc ( FILE * stream );
当我们需要读取文件然后将读取的内容输出到屏幕上时

在这里插入图片描述
注意这里的ch是int类型的,fgetc返回的是读取字符的ASCLL码

当我们删除一行ch = fgetc(pf)时,我们再来看看结果
在这里插入图片描述
结果是abcc,因为我们少了一行ch = fgetc(pf),导致状态指针没有向后移动,所以最后才会出现两个c

如果我们需要将文件所以的内容输出到屏幕上,我们只需要这样操作
在这里插入图片描述

fputs

fputs的详细介绍
函数格式 int fputs ( const char * str, FILE * stream );
现在我们用fputs将字符串写入文件中
在这里插入图片描述
我们也可以将字符串放入数组中,然后将数组内容写入文件
在这里插入图片描述

fgets

fgets的详细介绍
函数格式 char * fgets ( char * str, int num, FILE * stream );
我们再用fgets去读写刚刚写的文件
在这里插入图片描述
在这里插入图片描述
通过调试可以看到’\0’和’\n’也是被读取进去的,我们发现当读取的时候遇到’\n’时后面就全是’\0’,所以我们就得出,当遇到’\n’时就不会再往后读取数据了

而如果我们第一行读不完,比如我们只读取3个字符的话,那么就应该这样写
在这里插入图片描述
而如果我们需要读取第二行的字符的话,我们可以往后继续加fgets函数
在这里插入图片描述
但是如果我们第一行没有读完就想用上面的方法读第二行的话会有些问题的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

所有我们还需要再写一次fgets函数
在这里插入图片描述

fprintf

fprintf的详细介绍
函数格式 int fprintf ( FILE * stream, const char * format, … );
fprintf和printf是很相似的
比如我们现在需要将下面的结构体打印在屏幕上

struct S
{
	float f;
	char c;
	int n;
};
int main()
 {
	struct S s = { 3.14f,'w',100 };
	FILE* pf = fopen("jack.txt","w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	printf("%f,%c,%d",s.f,s.c,s.n);
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述

而fprintf的话我们只需要稍微加一点

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
struct S
{
	float f;
	char c;
	int n;
};
int main()
{
	struct S s = { 3.14f,'w',100 };
	FILE* pf = fopen("jack.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fprintf(pf,"%f,%c,%d", s.f, s.c, s.n);
   fclose(pf);
  pf = NULL;
  return 0;
}

在这里插入图片描述

这样我们就可以将数据写入文件了
另外,fprintf(pf,“%f,%c,%d”, s.f, s.c, s.n)中%f %c %d后面的逗号都会写入文件中,如果我们将逗号换成 - 也是会被写入文件的

struct S
{
	float f;
	char c;
	int n;
};
int main()
{
	struct S s = { 3.14f,'w',100 };
	FILE* pf = fopen("jack.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fprintf(pf,"%f-%c-%d", s.f, s.c, s.n);
   fclose(pf);
  pf = NULL;
  return 0;
}

在这里插入图片描述

fscanf

fscanf的详细介绍
函数格式 int fscanf ( FILE * stream, const char * format, … );
fscanf和scanf也是类似的

struct S
{
	float f;
	char c;
	int n;
};
int main()
{
	struct S s = {0};
	FILE* pf = fopen("jack.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	scanf("%f-%c-%d", &(s.f), &(s.c), &(s.n));
   fclose(pf);
  pf = NULL;
  return 0;
}

fscanf去读取文件的数据应该这样写

struct S
{
	float f;
	char c;
	int n;
};
int main()
{
	struct S s = {0};
	FILE* pf = fopen("jack.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fscanf(pf,"%f-%c-%d", &(s.f), &(s.c), &(s.n));
	printf("%f-%c-%d\n", s.f, s.c, s.n);
   fclose(pf);
  pf = NULL;
  return 0;
}

在这里插入图片描述

fwrite

fwrite的详细介绍
函数格式 size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
举个例子:

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	FILE*pf=fopen("jack.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fwrite(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

我们需要将数据的来源也就是arr拷贝到流里面去,所有我们需要传入数组的指针,以及数组每个元素的大小,需要拷贝多少个元素,以及文件指针流
在这里插入图片描述
这时候我们打开文件发现,文件里面的东西我们根本就看不懂,其实是因为这是二进制的输出
在这里插入图片描述
为了能够看懂文件里面的东西,我们就需要用下面的fread去读文件的内容

fread

fread的详细介绍
函数格式 size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
fread和fwrite的函数格式是一样的,因为fread是要从流里面读取然后放到数组arr中,每个元素的大小和读取的格式也是必须要知道的,所以他们的格式是一样的


int main()
{
	int arr[10] = { 0 };
	FILE*pf=fopen("jack.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fread(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

需要注意的是上一个是用wb,这里是用的rb
在这里插入图片描述

在这里插入图片描述

文件的随机读写

fseek

fseek的详细介绍
函数格式 int fseek ( FILE * stream, long int offset, int origin );
其中 long int offset是偏移量 int origin是起始位置
起始位置有三种情况
在这里插入图片描述
SEEK_SET是最开始的位置
SEEK_CUR是当前位置
SEEK_END是结束位置
为了理解我们举个例子

int main()
{
	FILE* pf = fopen("jack1.txt", "r");
		if (pf == NULL)
		{
			perror("fopen");
			return 1;
		}
		int ch=fgetc(pf);
		printf("%c\n", ch);//a

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

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

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

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

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

		ch = fgetc(pf);
		printf("%c\n", ch);//g
		fclose(pf);
		pf = NULL;
		return 0;
}

在这里插入图片描述

当我们再读一次的话结果就会打印i,但是如果现在需要你读取的指针再一次指向a,这时候我们就需要用到fseek

int main()
{
	FILE* pf = fopen("jack1.txt", "r");
		if (pf == NULL)
		{
			perror("fopen");
			return 1;
		}
		int ch=fgetc(pf);
		printf("%c\n", ch);//a

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

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

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

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

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

		ch = fgetc(pf);
		printf("%c\n", ch);//g
		fseek(pf,-7,SEEK_CUR);
		fclose(pf);
		pf = NULL;
		return 0;
}

因为a距离h差7,所以我们需要让文件指针偏移-7
我们也可以将fseek(pf,-7,SEEK_CUR)改成fseek(pf,0,SEEK_SET),这样的结果也是一样的

ftell

ftell的详细介绍
函数格式 long int ftell ( FILE * stream );
ftell是返回文件指针相对于起始位置的偏移量
以上面fseek的例子
在这里插入图片描述

rewind

rewind的详细介绍
函数格式 void rewind ( FILE * stream );
rewind是让当前指针位置回到起始
位置
在这里插入图片描述

文件读取结束的判定

在我们读取文件时我们不知道文件什么时候才会读取结束

被错误使用的 feof

feof

feof的详细介绍
函数格式int feof ( FILE * stream );
注意:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束
而是应用于当文件读取结束的时候,判断是读取失败结束的原因是否是遇到文件尾结束
文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如:
fgetc 判断是否为 EOF .(读取正常会返回一个字符的ASCLL码值,EOF就是读取结束)
fgets 判断返回值是否为 NULL .(读取成功会返回读取字符串的起始位置,失败会返回空指针)
2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:
fread判断返回值是否小于实际要读的个数
fread 要求读取count个大学为size字节的数据
如果真读取到count过数据,那就返回count
如果没有到话,就返回真实读取到的完整的数据个数

举个例子
如果一次需要读取5个元素,一个元素的大小为4个字节,因为要一次读取5个数据,所以我们读取的总个数是5的倍数,而总共元素个数却是16,因此当我们读取20个元素的时候,由于总个数小于要读取的个数,所以就会返回最后完整读取的元素1,因为1<5所以就结束了

补充:
ferror:装文件读取结束后,用来判断文件是否因为读取过程中遇到错误而结束
feof:在文件读取结束后,用来判断文件是否因为读取过程中遇到文件结束标志而结束

文本文件的例子

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

int main(void)
{
	int c;
	FILE* fp = fopen("test.txt", "r");
	if (!fp) {
		perror("File opening failed");
		return EXIT_FAILURE;
	}
	while ((c = fgetc(fp)) != EOF)
	{
		putchar(c);
	}
	if (ferror(fp))
		puts("I/O error when reading");
	else if (feof(fp))
		puts("End of file reached successfully");

	fclose(fp);
}

二进制文件的例子


#include <stdio.h>

enum { SIZE = 5 };
int main(void)
{
	double a[SIZE] = { 1.,2.,3.,4.,5. };
	FILE* fp = fopen("test.bin", "wb"); 
	fwrite(a, sizeof * a, SIZE, fp); 
	fclose(fp);

	double b[SIZE];
	fp = fopen("test.bin", "rb");
	size_t ret_code = fread(b, sizeof * b, SIZE, fp);
	if (ret_code == SIZE) {
		puts("Array read successfully, contents: ");
		for (int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
		putchar('\n');
	}
	else {
		if (feof(fp))
			printf("Error reading test.bin: unexpected end of file\n");
		else if (ferror(fp)) {
			perror("Error reading test.bin");
		}
	}

	fclose(fp);
}

文件缓冲区

ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。

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

如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数区(程序变量等)。缓冲区的大小根据C编译系统决定的。
在这里插入图片描述
简单的来说就是我们在程序数据区中写程序,比如int a=0 char c=‘a’…
我们将这些数据写入文件当中(也就是写入硬盘)

当我们将数据写入文件时,我们先将数据放入输出缓冲区,当输出缓冲区放满后在将这些数据放入硬盘中去

而当我们要读取文件的时候,就需要将硬盘的数据放入输入缓冲区,然后再读取

但是为什么不直接将硬盘当中的数据读取,非要经过一个输入缓冲区呢?并且要将程序数据写入文件时为什么又要经过输出缓冲区呢?

其实这是为了提高效率,当我们写文件的时候是需要调用操作系统接口的,而如果我们频繁调用操作系统接口的话就不利于效率

举个例子,自习课老师在讲台上帮同学们答疑,如果你一发现有不会的就上去问老师,这样老师的效率就很低,因为老师不能只照顾一个人,而如果你将你所有问题都整理好,一次性问完的话,这样效率就很高

下面的代码是验证缓冲区的存在

#include <stdio.h>
#include <windows.h>
//VS2013 WIN10环境测试
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;
}

标签:文件,ch,读取,int,pf,FILE,操作
From: https://blog.csdn.net/2401_86956109/article/details/142914221

相关文章

  • 禁止拷贝构造函数和赋值操作符
     GlobalSettings(constGlobalSettings&)=delete;GlobalSettings&operator=(constGlobalSettings&)=delete;这两行代码是为了禁止拷贝构造函数和赋值操作符,也就是说,GlobalSettings 类的对象无法通过拷贝或赋值来创建或修改。这是为了防止类的实例被复制,通常用于实现单......
  • Selenium操作:测试form表单
    from表单是经常测试的用例,用户登录、注册等都会用到form表单,本文简单设计了一个用户登录的form表单,并对该form表单进行测试一、自定义form表单1、用到的组件如下图,图中定义了一个登录界面的form表单,用到的表单元素:type="text";type="submit"2、代码示例新建HTML文件文......
  • ChtQuickDS.dll文件丢失导致程序无法运行问题
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个ChtQuickDS.dll文件(挑选合适的版本文件)把......
  • ChsStrokeDS.dll文件丢失导致程序无法运行问题
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个ChsStrokeDS.dll文件(挑选合适的版本文件)把......
  • VS新建文件时在头部附加版权信息
    用记事本打开“C:\ProgramFiles(x86)\MicrosoftVisualStudio\2017\Community\Common7\IDE\ItemTemplates\CSharp\Code\2052\Class\Class.cs”文件//----------------------------------------------------------------//Copyright(C)2017xx公司//文件名:$safeitemnam......
  • DAY50WEB 攻防-PHP 应用&文件包含&LFI&RFI&伪协议编码算法&无文件利用&黑白盒
    知识点:1、文件包含-原理&分类&危害-LFI&RFI2、文件包含-利用-黑白盒&无文件&伪协议文件包含-原理&分类&利用&修复原理:程序开发人员通常会把可重复使用的函数写到单个文件中,在使用某些函数时,直接调用此文件,而无须再次编写,这种调用文件的过程一般被称为文件包含。在包含文......
  • python实现了通过摄像头检测手部动作,根据手指数量的不同映射为特定的视频控制操作
    importcv2#导入OpenCV库,用于图像处理importmediapipeasmp#导入MediaPipe库,用于手部检测等fromseleniumimportwebdriver#导入selenium库fromselenium.webdriver.common.keysimportKeysfromselenium.webdriver.common.byimportByfromselenium.webdrive......
  • 【CTF Web】Pikachu SSRF(curl) Writeup(SSRF+读取文件+探测端口)
    SSRF(Server-SideRequestForgery:服务器端请求伪造)其形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制导致攻击者可以传入任意的地址来让后端服务器对其发起请求,并返回对该目标地址请求的数据数据流:攻击者-----......
  • 磁盘管理与文件系统
    磁盘管理与文件系统磁盘组成与分区磁盘的组成圆形的磁盘的圆盘机械手臂:负责读写圆盘上的数据主轴马达:转动磁盘,让机械手臂完成它的工作磁盘的圆盘组成扇区:最小物理存储单位,大小一般为512byte到4k不等分区表:分区表有两种,一种是限制较多的MBR分区表,其组成为MBR(Master......
  • 使用批处理对包含指定字符的文件进行批量重命名
    点击查看代码@echooffchcp65001rem使用批处理将文件夹下所有txt文件有11的字符重命名为22setlocalenabledelayedexpansion::遍历当前目录下的所有文件for%%Ain(*.txt)do(set"filename=%%~nxA"set"newfilename=!filename:11=22!"if"!filena......