C语言:深入理解文件操作

news2024/11/25 14:41:27

目录

1. 为什么使用文件?

2. 什么是文件?

2.1 程序文件

2.2 数据文件

2.3 文件名

3. 二进制文件和文本文件?

3.1测试代码:

4. 文件的打开和关闭

4.1 流和标准流

4.1.1 流

4.1.2 标准流 

4.2 文件指针

4.3 文件的打开和关闭

5. 文件的顺序读写

5.1 顺序读写函数介绍

fgetc函数使用

fputc函数使用

fgets函数使用

fputs函数使用

fscanf函数使用

fprintf函数使用

fread函数使用

fwrite函数使用

5.2与字符串有关联的两个函数:

sscanf函数使用

sprintf函数使用

6.文件的随机读写

6.1fseek

6.2ftell

6.3rewind

7.文件读取结束的判定

7.1 ferror及被错误使用的 feof 

8.文件缓冲区

9.完整的文件操作练习源码及运行结果


1. 为什么使用文件?

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

2. 什么是文件?

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

2.1 程序文件

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

2.2 数据文件

文件的内容不⼀定是程序,⽽是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。

平常的写代码时所处理数据的输⼊输出都是以终端为对象的,即从终端的键盘输⼊数据,运⾏结果显⽰到 显⽰器上。 其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使⽤,这⾥处 理的就是磁盘上⽂件。

2.3 文件名

⼀个⽂件要有⼀个唯⼀的⽂件标识,以便⽤⼾识别和引⽤。 文件名包含3部分:文件路径+文件名主干+文件后缀 例如: c:\code\test.txt

为了方便起见,⽂件标识常被称为文件名。

3. 二进制文件和文本文件?

根据数据的组织形式,数据文件被称为文本文件或者二进制文件。 数据在内存中以二进制的形式存储,如果不加转换的输出到外存的文件中,就是二进制文件。 如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。 ⼀个数据在文件中是怎么存储的呢? 字符⼀律以ASCII形式存储,数值型数据既可以⽤ASCII形式存储,也可以使⽤二进制形式存储。 如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符⼀个字节),而二进制形式输出,则在磁盘上只占4个字节(VS2022测试)。

3.1测试代码:


int main()
{
	int a = 10000;
	FILE * pf = fopen("test100.txt", "wb");
	fwrite(&a, 4, 1, pf);//⼆进制的形式写到⽂件中 
	fclose(pf);
	pf = NULL;
	return 0;
}

运行代码之后我们单击

打开文件

由于这是一个二进制文件不支持打开,那么我们把它添加进来

添加现有项:

单击二进制编辑器

4. 文件的打开和关闭

4.1 流和标准流

4.1.1 流

我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出 操作各不相同,为了⽅便程序员对各种设备进⾏⽅便的操作,我们抽象出了流的概念,我们可以把流 想象成流淌着字符的河。 C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。 ⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。

4.1.2 标准流 

那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢? 那是因为C语⾔程序在启动的时候,默认打开了3个流:

stdin标准输入流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。

stdout标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出 流中。

stderr标准错误流,⼤多数环境中输出到显⽰器界⾯。 这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。stdin、stdout、stderr三个流的类型是: FILE * ,通常称为⽂件指针。 C语⾔中,就是通过 FILE* 的⽂件指针来维护流的各种操作的。

4.2 文件指针

缓冲⽂件系统中,关键的概念是“⽂件类型指针”,简称“⽂件指针”。 每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名 字,⽂件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系 统声明的,取名FILE. 例如,VS2013编译环境提供的 stdio.h 头⽂件中有以下的⽂件类型声明:

struct _iobuf {
 char *_ptr;
 int _cnt;
 char *_base;
 int _flag;
 int _file;
 int _charbuf;
 int _bufsiz;
 char *_tmpfname;
};

typedef struct _iobuf FILE;

不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。 每当打开⼀个⽂件的时候,系统会根据⽂件的情况⾃动创建⼀个FILE结构的变量,并填充其中的信 息,使⽤者不必关⼼细节。 ⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使⽤起来更加⽅便。下⾯我们可以创建⼀个FILE*的指针变量:

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

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

4.3 文件的打开和关闭

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

//打开⽂件 
FILE * fopen ( const char * filename, const char * mode );

//关闭⽂件 

int fclose ( FILE * stream );

mode表示文件的打开模式,下⾯都是⽂件的打开模式:

实例代码:

/* fopen fclose example */

#include <stdio.h>

int main ()
{
 FILE * pFile;
 //打开⽂件 
 pFile = fopen ("myfile.txt","w");
 //⽂件操作 
 if (pFile!=NULL)
 {
 fputs ("fopen example",pFile);
 //关闭⽂件 
 fclose (pFile);
 }
 return 0;
}

5. 文件的顺序读写

5.1 顺序读写函数介绍

上⾯说的适⽤于所有输⼊流⼀般指适⽤于标准输⼊流和其他输⼊流(如⽂件输⼊流);所有输出流⼀ 般指适⽤于标准输出流和其他输出流(如⽂件输出流)。

fgetc函数使用

int fgetc ( FILE * stream );

参数:

stream:指向标识输入流的 FILE 对象的指针。

//fgetc读
	FILE* pfr = fopen("test.txt", "r");
	if (pfr == NULL)
	{
		perror("fopen pfr test.txt fail!");
		return 1;
	}
	int chr = 0;
	while ((chr = fgetc(pfr)) != EOF)
	{
		printf("%c ", chr);
	}
	fclose(pfr);
	pfr = NULL;

从流中一个字符一个字符读取,读取成功后返回该字符的ASCII码值,直到读取到最后一个字符返回EOF(-1)

fputc函数使用

int fputc ( int character, FILE * stream );

参数:

character:要编写的字符的 整型类型。
写入时,该值在内部转换为无符号字符。

stream:指向标识输出流的 FILE 对象的指针。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
//fputc写
	FILE* pfw = fopen("test.txt", "w");
	if (pfw == NULL)
	{
		perror("fopen pfw test.txt fail!");
		return 1;
	}
	int chw = 0;
	for (chw = 'a'; chw <= 'z'; chw++)
	{
		printf("%c ", chw);
		fputc(chw, pfw);
	}
	fclose(pfw);
	pfw = NULL;
   


   return 0;
}

将a-z26个字符一个一个写入流中

fgets函数使用

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

参数:

str:

指向复制字符串读取的 char数组的指针。

num:

要复制到 str 中的最大字符数(包括终止 null 字符)。

stream:

指向标识输入流的 FILE 对象的指针。
stdin 可以用作从标准输入读取的参数。

//fgets读
FILE* pfr2 = fopen("test1.txt", "r");
if (pfr2 == NULL)
{
	perror("fopen pfr2 test1.txt fail");
	return 1;
}
char arr[50] = { 0 };
fgets(arr, 19, pfr2);
printf("%s", arr);
fclose(pfr2);
pfr2 = NULL;

把流中的字符串中的18个字符读过来,最后赋一个'\0',所以这里注意,19的含义

fputs函数使用

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

参数:

str:

包含要写入的内容的 C 字符串

stream:

指向标识输出流的 FILE 对象的指针。

	//fputs写
	FILE* pfw2 = fopen("test1.txt", "w");
	if (pfw2 == NULL)
	{
		perror("fopen pfw2 test1.txt fail!");
		return 1;
	}
	char Array[50] = { "这是一个字符串" };
	printf("%s",Array);
	fputs(Array, pfw2);
	fclose(pfw2);
	pfw2 = NULL;

将一个字符串写入流中

fscanf函数使用

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

参数:

stream:

指向 FILE 对象的指针,该对象标识要从中读取数据的输入流。

format:格式(和scanf函数一样)...

//fscanf读
FILE* pfr3 = fopen("test2.txt", "r");
if (pfr3 == NULL)
{
	perror("fopen pfr3 test2.txt fail");
	return 1;
}
int array[19] = { 0 };
for (i = 0; i < sizeof(array) / sizeof(array[0]); i++)
{
	fscanf(pfr3, "%d", &array[i]);
	printf("%d ", array[i]);
}

fclose(pfr3);
pfr3 = NULL;

将格式化的数据从流中读取出来存放入array中

fprintf函数使用

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

参数:

stream:指向标识输出流的 FILE 对象的指针。

format:格式(和printf函数一样)...

//fprintf写
FILE* pfw3 = fopen("test2.txt", "w");
if (pfw3 == NULL)
{
	perror("fopen pfw3 test2.txt fail");
	return 1;
}
int arr1[19] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
for (i = 0; i < sizeof(arr1) / sizeof(arr1[0]); i++)
{
	printf("%d ", arr1[i]);
	fprintf(pfw3, "%d ", arr1[i]);
}
fclose(pfw3);
pfw3 = NULL;

将格式化的数据写入流中

fread函数使用

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

参数:

ptr:

指向大小至少为 (size*count) 字节的内存块的指针,该内存块已转换为 void*。

size:

要读取的每个元素的大小(以字节为单位)。
size_t 是无符号整数类型。

count:

元素的数量,每个元素的大小为 bytes
size_t 是无符号整数类型。

stream:

指向指定输入流的 FILE 对象的指针。

//fread读取二进制文本
FILE* wrpf = fopen("binary.txt", "rb");
if (wrpf == NULL)
{
perror("fopen wrpf binary.txt fail");
return 1;
}
int rarr[11] = { 0 };
i = 0;
while (fread(&rarr[i], sizeof(int), 1, wrpf))
{
	printf("%d ", rarr[i++]);
}
fclose(wrpf);
wrpf = NULL;

把流中的二进制数据读取出来,然后格式化打印出可看懂的数据

fwrite函数使用

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

参数:

ptr:

指向要写入的元素数组的指针,将其转换为 const void*。

size:

要写入的每个元素的大小(以字节为单位)。
size_t 是无符号整数类型。

count:

元素的数量,每个元素的大小为 bytes
size_t 是无符号整数类型。

stream:

指向指定输出流的 FILE 对象的指针。

//fwrite二进制写入
FILE* wbpf = fopen("binary.txt","wb");
if (wbpf == NULL)
{
	perror("fopen wbpf binary.txt fail");
	return 1;
}

int warr[10] = { 1,2,3,4,5,6,7,8,9,10 };
fwrite(warr, sizeof(int), 5, wbpf);
fclose(wbpf);
wbpf = NULL;

将数据以二进制的形式写入流中

5.2与字符串有关联的两个函数:

sscanf函数使用

int sscanf ( const char * s, const char * format, ...);

参数:

s:

函数作为其源处理以检索数据的 C 字符串。

format:

C 字符串,包含一个格式字符串,该字符串遵循与 scanf 中的格式相同的规范(有关详细信息,请参阅 scanf)。

...(附加参数)

根据格式字符串,该函数可能需要一系列附加参数,每个参数都包含指向已分配存储的指针,其中提取的字符的解释以适当的类型存储。
这些参数的数量至少应与格式说明符存储的值数一样多。该函数将忽略其他参数。

//sscanf读
student s2 = { 0 };
sscanf(buf, "%s%d%s", s2.name, &(s2.age), s2.gender);
printf("以格式化形式打印s2:%s %d %s\n", s2.name, s2.age, s2.gender);

将字符串形式的数据,以格式化的形式读取出来让结构体s2接收

sprintf函数使用

int sprintf ( char * str, const char * format, ... );

参数:

str

指向存储生成的 C 字符串的缓冲区的指针。
缓冲区应足够大,以包含生成的字符串。

格式

C 字符串,包含一个格式字符串,该字符串遵循与 printf 中的格式相同的规范(有关详细信息,请参阅 printf)。

...(附加参数)

根据格式字符串,该函数可能需要一系列附加参数,每个参数都包含一个值,用于替换格式字符串中的格式说明符(或指向存储位置的指针,对于 n)。
这些参数的数量至少应与格式说明符中指定的值数一样多。该函数将忽略其他参数。

//sprintf写
FILE* swpf = fopen("text.txt3", "w");
if (swpf == NULL)
{
	perror("fopen swpf text.txt3 fail!");
	return 1;
}
student s1 = { "张三",19,"男" };
printf("以格式化形式打印s1:%s %d %s\n", s1.name, s1.age, s1.gender);
char buf[200] = { 0 };
sprintf(buf,"%s %d %s", s1.name, s1.age, s1.gender);
printf("以字符串形式打印s1:%s",buf);
fprintf(swpf,"%s ", buf);
fclose(swpf);
swpf = NULL;

将格式化形式的数据,转换成字符串形式的数据

6.文件的随机读写

6.1fseek

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

根据⽂件指针的位置和偏移量来定位⽂件指针(⽂件内容的光标)。

//fseek随机读写
FILE* fseekpf = fopen("fseek.txt", "r");
if (fseekpf == NULL)
{
	perror("fopen fseekpf fseek.txt fail!");
	return 1;
}
fseek(fseekpf, 4, SEEK_SET);
int ch=fgetc(fseekpf);
printf("%c ",ch);

参数:

stream

指向标识流的 FILE 对象的指针。

offset

二进制文件:要从偏移的字节数。
文本文件:零,或 ftell 返回的值。

origin

用作偏移量参考的位置。它由 <cstdio> 中定义的以下常量之一指定,专门用作此函数的参数:

不断参考位置
SEEK_SET文件开头
SEEK_CUR文件指针的当前位置
SEEK_END文件结束 *

*允许库实现不能有意义地支持 SEEK_END(因此,使用它的代码没有真正的标准可移植性)。

6.2ftell

long int ftell ( FILE * stream );

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

参数:

stream

指向标识流的 FILE 对象的指针。

//ftell返回文件指针相对于起始位置的偏移量
printf("\n%d", ftell(fseekpf));

6.3rewind

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

参数:

stream

指向标识流的 FILE 对象的指针。

//rewind 将文件指针指向初始位置
rewind(fseekpf);

7.文件读取结束的判定

7.1 ferror及被错误使用的 feof 

int ferror ( FILE * stream );
int feof ( FILE * stream );

参数:

stream

指向标识流的 FILE 对象的指针。

牢记:在文件读取过程中,不能⽤feof函数的返回值直接来判断⽂件的是否结束。

feof 的作⽤是:当⽂件读取结束的时候,判断是读取结束的原因是否是:遇到⽂件尾结束。

1. ⽂本⽂件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets ) 例如:

 fgetc 判断是否为 EOF .

 fgets 判断返回值是否为 NULL .

2. ⼆进制⽂件的读取结束判断,判断返回值是否⼩于实际要读的个数。 例如:

fread判断返回值是否⼩于实际要读的个数。

example:

#include <stdio.h>

#include <stdlib.h>


int main(void)
{
 int c; // 注意:int,⾮char,要求处理EOF 
 FILE* fp = fopen("test.txt", "r");
 if(!fp) {
 perror("File opening failed");
 return EXIT_FAILURE;
 }
 //fgetc 当读取失败的时候或者遇到⽂件结束的时候,都会返回EOF 
 while ((c = fgetc(fp)) != EOF) // 标准C I/O读取⽂件循环 
 { 
 putchar(c);
 }
 //判断是什么原因结束的 
 if (ferror(fp))
 puts("I/O error when reading");
 else if (feof(fp))
 puts("End of file reached successfully");
 
 fclose(fp);
}

8.文件缓冲区

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

#include <stdio.h>

#include <windows.h>

//VS2022 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语⾔在操作⽂件的时候,需要做刷新缓冲区或者在⽂件操作结束的时候关闭文件

如果不做,可能导致读写⽂件的问题。

9.完整的文件操作练习源码及运行结果

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
typedef struct student
{
	char name[20];
	int age;
	char gender[10];
}student;
int main()
{
//fputc写
	FILE* pfw = fopen("test.txt", "w");
	if (pfw == NULL)
	{
		perror("fopen pfw test.txt fail!");
		return 1;
	}
	int chw = 0;
	for (chw = 'a'; chw <= 'z'; chw++)
	{
		printf("%c ", chw);
		fputc(chw, pfw);
	}
	fclose(pfw);
	pfw = NULL;
//换行
	printf("\n");

//fgetc读
	FILE* pfr = fopen("test.txt", "r");
	if (pfr == NULL)
	{
		perror("fopen pfr test.txt fail!");
		return 1;
	}
	int chr = 0;
	while ((chr = fgetc(pfr)) != EOF)
	{
		printf("%c ", chr);
	}
	fclose(pfr);
	pfr = NULL;
//换行
	printf("\n");
	
	//fputs写
	FILE* pfw2 = fopen("test1.txt", "w");
	if (pfw2 == NULL)
	{
		perror("fopen pfw2 test1.txt fail!");
		return 1;
	}
	char Array[50] = { "这是一个字符串" };
	printf("%s",Array);
	fputs(Array, pfw2);
	fclose(pfw2);
	pfw2 = NULL;

	//换行
	printf("\n");

	//fgets读
	FILE* pfr2 = fopen("test1.txt", "r");
	if (pfr2 == NULL)
	{
		perror("fopen pfr2 test1.txt fail");
		return 1;
	}
	char arr[50] = { 0 };
	fgets(arr, 19, pfr2);
	printf("%s", arr);
	fclose(pfr2);
	pfr2 = NULL;

	//换行
	printf("\n");

	//fprintf写
	FILE* pfw3 = fopen("test2.txt", "w");
	if (pfw3 == NULL)
	{
		perror("fopen pfw3 test2.txt fail");
		return 1;
	}
	int arr1[19] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	for (i = 0; i < sizeof(arr1) / sizeof(arr1[0]); i++)
	{
		printf("%d ", arr1[i]);
		fprintf(pfw3, "%d ", arr1[i]);
	}
	fclose(pfw3);
	pfw3 = NULL;


	//换行
	printf("\n");
	
	
	//fscanf读
	FILE* pfr3 = fopen("test2.txt", "r");
	if (pfr3 == NULL)
	{
		perror("fopen pfr3 test2.txt fail");
		return 1;
	}
	int array[19] = { 0 };
	for (i = 0; i < sizeof(array) / sizeof(array[0]); i++)
	{
		fscanf(pfr3, "%d", &array[i]);
		printf("%d ", array[i]);
	}
	
	fclose(pfr3);
	pfr3 = NULL;



	//换行
	printf("\n");
	
	//sprintf写
	FILE* swpf = fopen("text.txt3", "w");
	if (swpf == NULL)
	{
		perror("fopen swpf text.txt3 fail!");
		return 1;
	}
	student s1 = { "张三",19,"男" };
	printf("以格式化形式打印s1:%s %d %s\n", s1.name, s1.age, s1.gender);
	char buf[200] = { 0 };
	sprintf(buf,"%s %d %s", s1.name, s1.age, s1.gender);
	printf("以字符串形式打印s1:%s",buf);
	fprintf(swpf,"%s ", buf);
	fclose(swpf);
	swpf = NULL;

	//换行
	printf("\n");

	//sscanf读
	student s2 = { 0 };
	sscanf(buf, "%s%d%s", s2.name, &(s2.age), s2.gender);
	printf("以格式化形式打印s2:%s %d %s\n", s2.name, s2.age, s2.gender);



	//换行
	printf("\n");
	//fwrite二进制写入
	FILE* wbpf = fopen("binary.txt","wb");
	if (wbpf == NULL)
	{
		perror("fopen wbpf binary.txt fail");
		return 1;
	}

	int warr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	fwrite(warr, sizeof(int), 5, wbpf);
	fclose(wbpf);
	wbpf = NULL;

	//fread读取二进制文本
	FILE* wrpf = fopen("binary.txt", "rb");
	if (wrpf == NULL)
	{
	perror("fopen wrpf binary.txt fail");
	return 1;
	}
	int rarr[11] = { 0 };
	i = 0;
	while (fread(&rarr[i], sizeof(int), 1, wrpf))
	{
		printf("%d ", rarr[i++]);
	}
	//换行
	printf("\n");
	//feof和ferror判断文件是否是读取正常结束或读取异常结束
	if (feof(wrpf))
	{
		printf("文件读取正常结束\n");
	}
	else if (ferror(wrpf))
	{
		printf("文件读取异常结束\n");

	}
	fclose(wrpf);
	wrpf = NULL;

	//换行
	printf("\n");

	//fseek随机读写
	FILE* fseekpf = fopen("fseek.txt", "r");
	if (fseekpf == NULL)
	{
		perror("fopen fseekpf fseek.txt fail!");
		return 1;
	}
	fseek(fseekpf, 4, SEEK_SET);
	int ch=fgetc(fseekpf);
	printf("%c ",ch);
	
	//ftell返回文件指针相对于起始位置的偏移量
	printf("\n%d", ftell(fseekpf));

	//rewind 将文件指针指向初始位置
	rewind(fseekpf);
	//ftell返回文件指针相对于起始位置的偏移量
	printf("\n%d", ftell(fseekpf));

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2063536.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【刷题笔记】二叉树2

1 二叉树的层序遍历 上一期我们讲了关于二叉树的前序、中序以及后序遍历的相关内容。然而&#xff0c;还存在一种遍历方式&#xff0c;这种方式非常符合我们人类的正常思维&#xff0c;可以求解很多树相关的问题&#xff0c;比较暴力——二叉树的层序遍历。 二叉树的层序遍历与…

股票买卖的思路与代码

题目 1302&#xff1a;股票买卖 时间限制: 1000 ms 内存限制: 65536 KB 提交数:8660 通过数: 4290 【题目描述】 最近越来越多的人都投身股市&#xff0c;阿福也有点心动了。谨记着“股市有风险&#xff0c;入市需谨慎”&#xff0c;阿福决定先来研究一下简化版的股…

文华软件自动画线 参数自调 多空波段变色线(源码自取)

编写思路 想要随意输入一个点位&#xff0c;即可按照这个点位自动画线&#xff0c;此线可以多空变色&#xff0c;上下突破线时箭头提示并发出声音预警。 代码函数重点解析 A、DRAWSL 绘制直线&#xff08;段&#xff09;。 用法&#xff1a; DRAWSL(COND,DATA,SLOPE,LEN,E…

【OpenCV】111

1 新建项目 新建项目&#xff0c;路径不要出现中文 文件夹名称 添加解释器&#xff0c;添加本地解释器 这样就创建好了一个文件夹&#xff0c;然后像我这样一级一级向下分&#xff0c;细分文件夹

TinyC编译器5—词法分析

1.词法分析的基本概念 词法分析也称为分词&#xff0c;此阶段编译器从左向右扫描源文件&#xff0c;将其字符流扫描分割成一个个的词&#xff08;记号、token&#xff09;。所谓token&#xff0c;就是源文件中不可再进一步分割的一串字符&#xff0c;类似英语中的单词&#xf…

Transformer大模型在训练过程中所需的计算量

目录 简介计算需求参数与数据集的权衡计算成本的工程意义内存需求推理模型权重总推理内存训练模型参数优化器状态梯度激活值和批大小总训练内存分布式训练分片优化器3D 并行分片优化器 + 3D 并行参考简介 许多关于Transformer语言模型的基本且重要的信息都可以用相当简单的方式…

基于微信小程序的大用户心理咨询系统设计与实现---附源码99040

目录 1 绪论 1.1 研究背景 1.2研究现状 1.3论文结构与章节安排 2 基于微信小程序的大用户心理咨询系统设计与实现分析 2.1 可行性分析 2.2 系统功能分析 2.3 系统用例分析 2.4 系统流程分析 2.5本章小结 3 基于微信小程序的大用户心理咨询系统设计与实现总体设计 3.…

网站首页配置-记录部分错误

目录 错误问题1: 解决方案: 错误问题2: 解决方案&#xff1a; 错误问题3&#xff1a; 解决方案&#xff1a; 错误问题4&#xff1a; 解决方案&#xff1a; EL的作用: 错误问题1: 解决方案: 里面的代码写错&#xff0c;cateSecond应该写成categorySecond 错误问题2: 解…

toRef 与 toRefs

在 ref函数与reactive函数的对比 这一篇博文中&#xff0c;我们从使用角度对比了 ref 与 reactive 的区别&#xff0c;最终得出结论是&#xff0c; 通过 ref 定义的数据&#xff0c;在 js脚本中使用需要 xxx.value &#xff0c;在模板中会自动解包&#xff0c;可以直接使用通过…

WIN 10 添加右键菜单(VSCode 打开当前目录)

WIN 10 添加右键菜单&#xff08;VSCode 打开当前目录&#xff09; 前言最终效果操作步骤 前言 每次打开代码都需要先打开 VSCode&#xff0c;再选择最近打开的项目或者浏览打开项目&#xff0c;感觉比较难找。所以自己添加了右键命令。 最终效果 操作步骤 cmd 打开注册表 找…

20240821给飞凌OK3588-C的核心板刷Rockchip原厂的Buildroot并启动

20240821给飞凌OK3588-C的核心板刷Rockchip原厂的Buildroot并启动 2024/8/21 15:22 viewproviewpro-ThinkBook-16-G5-IRH:~/repo_RK3588_Buildroot20240508$ viewproviewpro-ThinkBook-16-G5-IRH:~/repo_RK3588_Buildroot20240508$ ./build.sh lunch 3. rockchip_rk3588_evb7_…

C++智能指针的用法(全)

一、智能指针概念 C/C 语言最为人所诟病的特性之一就是存在内存泄露问题&#xff0c;因此后来的大多数语言都提供了内置内存分配与释放功能&#xff0c;有的甚至干脆对语言的使用者屏蔽了内存指针这一概念。这里不置贬褒&#xff0c;手动分配内存与手动释放内存有利也有弊&…

普元EOS-基于CriteriaEntity进行数据查询

1 前言 普元EOS内置了一系列数据库的操作类&#xff0c;本文介绍其中的一个类 CriteriaEntity的使用方法。 CriteriaEntity是进行组织数据库查询条件的类&#xff0c;基于该类配合DataObject&#xff0c;实现对数据库的查询。 2 CriteriaType类的实例化 要利用Criteria进行查…

LlamaIndex 实现 RAG (一)

理解过 LlamaIndex 的功能之后&#xff0c;本文通过 LlamaIndex 快速实现一个简单的 RAG 应用&#xff0c;主要包括以下几个部分&#xff1a; 创建知识库&#xff0c;并进行 Embedding集成本地 Ollama 模型或者 Qwen 模型通过 Streamlit 可视化 RAG 文末提供了源代码地址 创…

HarmonyOS开发实战:应用权限/通知设置跳转方案

场景描述 引导用户跳转到系统设置页进行权限&#xff0c;通知的相关设置&#xff0c;类似android和iOS应用中常见的应用内跳转到设置进行通知开启或权限设置的操作。 应用经常会遇到如下的业务诉求&#xff1a; 场景一&#xff1a;如果应用首次拒绝了消息通知&#xff0c;应…

免费高效:2024年四大视频剪辑软件推荐!

不管是不是专业人士&#xff0c;相信大家多多少少都会有视频剪辑的需求&#xff0c;对于很多新手来说&#xff0c;一款好用且免费的视频剪辑工具十分必要&#xff0c;接下来就为大家推荐几个好用的视频剪辑免费软件&#xff01; 福昕视频剪辑 链接&#xff1a;www.pdf365.cn/…

Linux(CentOS7)虚拟机安装教程

创建虚拟机 自定义高级&#xff0c;就下一步 选择Workstation 17.x,完好后就继续下一步,下面就如图所示 虚拟机内存看情况加 磁盘大小也看情况加 完成&#xff01; 开启此虚拟机 鼠标放进去直接回车 可能有点慢&#xff0c;请耐心等待 一.进入日期时间 二.进入软件选择 三.配置…

[创业之路-138] :产品需求、产品研发、产品生产、库存管理、品控、售后全流程 - 时序图

目录 一、产品研发全流程 1. 客户/市场需求 2. 供应链采购 3. 设计研发 4. 库房管理 5. 品控质检 6. 物流运输 7. 客户现场验证 8. 返修售后 二、产品生产全流程 1. 客户/市场需求 2. 供应链采购 3. 生产加工 4. 库房管理 5. 品控质检 6. 物流运输 7. 客户现场…

物理可微分神经算法:深度学习与物理世界的桥梁

物理可微分神经算法&#xff1a;深度学习与物理世界的桥梁 前言物理可微分神经算法的核心PyTorch中的实现讨论与展望结语 前言 在这个信息爆炸的时代&#xff0c;人工智能&#xff08;AI&#xff09;已成为推动技术革新的关键力量。深度学习&#xff0c;作为AI领域的一个重要分…

CAPL如何实现在网络节点中添加路由Entry

其实不只是CANoe的网络节点,所有设备的应用程序如果要通过Socket套接字发送报文,在网络层都需要根据路由表里配置的路由条目选择发送路径。这个路由条目可以是静态配置,也可以是自动添加。 如果CANoe的网络节点添加一个网络接口,配置IP地址和子网掩码: 说明此网络节点在1…