目录

1. 什么是文件

1.1 程序文件

1.2 数据文件

1.3 文件名 

2. 二进制文件和文本文件

3. 文件的打开与关闭

3.1 流和标准流

3.2 文件指针

3.3 文件的打开与关闭

fopen 

fclose

4. 文件的顺序读写

4.1 fgetc和fputc 

 fgetc

fputc

4.2 fgets和fputs

fgets

fputs 

4.3 scanf / fscanf / sscanf

scanf 

fscanf

sscanf

4.4 printf / sprintf / fprintf

printf

fprintf

 sprintf

4.5 fread和fwrite

fread

fwrite

5. 文件的随机读写

fseek

ftell

rewind

6. 文件读取结束的判定

7. 文件缓冲区


1. 什么是文件

磁盘(硬盘)上的文件就是文件

        
但是在程序设计中,我们⼀般谈的文件有两种:程序文件、数据文件

1.1 程序文件

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

1.2 数据文件

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

1.3 文件名 

⼀个文件要有⼀个唯⼀的文件标识(文件标识常被称为文件名),以便用户识别和引用,文件名包含3部分:文件路径 + 文件名主干 + 文件后缀

例如: c:\code\test.txt

c:\code\:表示文件路径      test:表示文件名主干      .txt:表示文件后缀


2. 二进制文件和文本文件

根据数据的组织形式,数据文件被称为文本文件和二进制文件

        

二进制文件二进制文件就是把内存中的数据按其在内存中存储的形式原样存储在磁盘上

        

文本文件:如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的⽂件就是文本文件

 

数据在内存中如何存储呢?

        

字符⼀律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式储

        

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

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int a = 100;
	FILE* pa = fopen("text.txt", "wb");
	fwrite(&a, 4, 1, pa);
	fclose(pa);
	pa == NULL;

	return 0;
}


3. 文件的打开与关闭

        

3.1 流和标准流

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

        

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

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

        
stderr - 标准错误流,⼤多数环境中输出到显示器界⾯

        

头文件为:

        

#include<stdio.h>

stdin、stdout、stderr 三个流的类型是: FILE* ,通常称为文件指针

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

3.2 文件指针

文件类型指针简称件指针

每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE

我们可以在编译环境提供的 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*pf;

定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)

        

通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件

3.3 文件的打开与关闭

文件在读写之前应该先打开文件,在使用结束之后应该关闭文件

        

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

        

 ANSIIC规定使用fopen 函数来打开文件, fclose 来关闭文件

fopen 

fopen文档链接:

        

fopen - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/fopen/?kw=fopen

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

其功能是使用给定的模式 mode 打开 filename 所指向的文件。文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回 NULL,并把错误代码存在 error 中

fclose

fclose文档链接:

        

fclose - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/fclose/?kw=fclose

//关闭⽂件
int fclose ( FILE * stream );

返回值如果流成功关闭,fclose 返回 0,否则返回EOF(-1)(如果流为NULL,而且程序可以继续执行,fclose设定error number给EINVAL,并返回EOF)

当时用"w"、"wb"、"w+"、"wb+"这些打开方式时会清除文件原本存储的数据 

/* 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;
}


4. 文件的顺序读写

4.1 fgetc和fputc 

 fgetc

fgetc文档链接:

        

fgetc - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/fgetc/?kw=fgetc

int fgetc ( FILE * stream );

从文件指针stream指向的文件中读取一个字符,该字符的ASCII值作为函数的返回值,读取一个字节后光标向后移一个字节,若返回值为EOF,说明文件结束,EOF是文件结束标志,值为-1 

int main()
{
	FILE* pb = fopen("text.txt", "r");//为输入打开一个文本文件
	if (pb == EOF)//判断是否打开成功
	{
		perror("fopen");
		return 1;
	}

	int ch = 0;
	while ((ch = fgetc(pb)) != EOF)//判断是否成功输入数据
	{
		printf("%c", ch);//成功读取则将外部文件中的数据输入到程序中
	}

	printf("\n");
	fclose(pb);//关闭文件
	pb == NULL;//将pb置为空指针

	return 0;
}

fputc

fputc文档链接:

        

fputc - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/fputc/?kw=fputc

int fputc ( int character, FILE * stream );

返回值:

        

在正常调用情况下,函数返回写入文件的字符的ASCII码值,出错时,返回EOF(-1)。当正确写入一个字符或一个字节的数据后,文件内部写指针会自动后移一个字节的位置

int main()
{
	FILE* pa = fopen("text.txt", "w");//打开文件,打开方式为输出数据w
	if (pa == NULL)//判断是否打开成功
	{
		perror("fopen");
		return 1;
	}

	for (int i = 0; i < 26; i++)//成功打开后则把程序中的数据输出到外部文件中
	{
		int ret = fputc('a' + i, pa);
		printf("%c", ret);
	}

    //判断是否关闭文件成功
	if (fclose(pa) == EOF)
	{
		perror("fclose");
		return 1;
	}

	pa == NULL;//将pa置为空指针,防止野指针

	return 0;

4.2 fgets和fputs

 

fgets

fgets文档链接:

        

fgets - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/fgets/?kw=fgets

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

从指定的流 stream 读取一行(每次最多只能从文件中读取一行内容,因为 fgets遇到换行符就结束读取,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时或者读取到换行符时又或者到达文件末尾时,它会停止,具体视情况而定

        

如果文件中的该行,不足n-1个字符,则读完该行就结束。如若该行(包括最后一个换行符)的字符数超过n-1,则fgets只返回一个不完整的行

返回值:

        

1. 如果成功,该函数返回str的头指针

        

2. 如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针

        

3. 如果发生错误,返回一个空指针

int main()
{
	FILE* pc = fopen("text.txt", "r");//为了输入数据打开文件
	if (pc == NULL)//判断是否打开成功
	{
		perror("fopen");
		return 1;
	}

	char arr[30] = { 0 };//创造数组
	//将读取到的字符串串存储到arr指向的字符串内,如果外部文件中有两行字符串,只需再读取一次
	fgets(arr, 26 + 1, pc);
	puts(arr);             //打印字符串
	fclose(pc);//关闭文件
	pc = NULL;//置为空指针

	return 0;
}

fputs 

fputs文档链接:

        

fputs - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/fputs/?kw=fputs

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

str:一个数组,包含了要写入的以空字符终止的字符序列

stream:指向 FILE 对象的指针,该 FILE 对象标识了要被写入字符串的流

函数功能是向指定的文件写入一个字符串(不自动写入字符串结束标记符‘\0’),成功写入一个字符串后,文件的位置指针会自动后移

        

该函数返回一个非负值,如果发生错误则返回 EOF(-1)

int main()
{
	FILE* pd = fopen("1.txt", "w");//为了将程序中的数据输入到外部文件,打开文件
	if (pd == NULL)//判断
	{
		perror("fopen");
		return 1;
	}

	char arr[20] = "sjjwdjtmkadkiana";//创建数组
	if ((fputs(arr, pd)) == EOF)//判断是否输入成功
	{
		perror("fputs");
		return 1;
	}
	
	if (fclose(pd) == EOF)//判断是否关闭成功
	{
		perror("fclose");
		return 1;
	}

	pd = NULL;
	return 0;
}

4.3 scanf / fscanf / sscanf

函数 功能
scanf 从标准输入流上读取格式化的数据
fscanf 从指定的输入流上读取格式化的数据
sscanf 在字符串中读取格式化的数据

         

scanf 

scanf文档链接:

        

cplusplus.com/reference/cstdio/scanf/https://cplusplus.com/reference/cstdio/scanf/

int scanf ( const char * format, ... );

 

函数的第一个参数是格式字符串,它指定了输入的格式,并按照格式说明符解析输入对应位置的信息并存储于可变参数列表中对应的指针所指位置

        

每一个指针要求非空,并且与字符串中的格式符一一顺次对应

fscanf

fscanf文档链接:

        

fscanf - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/fscanf/?kw=fscanf        

fscanf:从指定的输入流上读取格式化的数据

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

stream:要读取的文件指针,可以是标准输入流stdin、文件指针或者其他已经打开的文件流

format:格式控制字符串,指定了要读取的数据的格式和顺序

...:可变参数列表,用于接收读取的数据

fscanf其功能为根据数据格式(format),从输入流(stream)中读入数据,存储到...中,遇到空格和换行时结束,这与fgets有区别,fgets遇到空格不结束

int main()
{
	FILE* pb = fopen("text.txt", "r");//打开文件
	//判断是否打开成功
	if (pb == NULL)                   
	{
		perror("fopen1");
		return 1;
	}
	char b = 0; 
	char arr[10] = { 0 };

	//从文件流中读取数据格式化后分别放到b和arr中
	fscanf(pb, "%c\n%s", &b, arr);    

	printf("%c\n", b);                   
	printf("%s", arr);

	fclose(pb);                        
	pb = NULL;                         

	return 0;
}

sscanf

sscanf文档链接:

        

sscanf - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/sscanf/?kw=sscanf        

sscanf:在字符串中读取格式化的数据

 

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

str:要解析的字符串      

format:格式字符串      

...:可变参数列表,用于接收解析后的数据

 返回值函数将返回成功赋值的字段个数,失败返回0 ,否则返回格式化的参数个数

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

int main()
{
	char arr[] = "kiana";
	char arr1[10] = { 0 };

	//从字符串arr中读取数据放入arr1中
	sscanf(arr, "%s", arr1);
	printf("%s\n", arr1);

	int n = 0;

	sscanf("kiana 1134", "%s %d", arr1, &n);
	printf("%s %d", arr1, n);

	return 0;
}

sscanf的优点:

        

sscanf函数可以根据格式字符串的规则解析不同类型的数据,具有很高的灵活性

        

在使用sscanf函数时,要确保格式字符串与要解析的数据格式匹配,否则可能会导致解析错误或未定义的行为

        

需要注意的是,sscanf函数只会从字符串中解析数据,不会对字符串进行修改

4.4 printf / sprintf / fprintf

函数 功能
printf       把数据以格式化的形式打印在标准输出流上
fprintf 把数据以格式化的形式打印在指定的输出流上
sprintf 把格式化的数据转化成字符串

printf

printf文档链接:

        

printf - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/printf/?kw=printf        

printf:把数据以格式化的形式打印在标准输出流上

int printf ( const char * format, ... );

fprintf

fprintf文档链接:

        

fprintf - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/fprintf/?kw=fprintf        

fprintf:把数据以格式化的形式打印在指定的输出流上

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

函数功能:根据指定的格式(format),向输出流(比如文件流)写入数据

        

返回值:如果写入文件成功,则返回写入的总字符数,如果写入文件失败,则返回负值

int main()
{
	FILE* pa = fopen("text.txt", "w");
	if (pa == NULL)
	{
		perror("fopen");
		return 1;
	}

	char arr[] = "kiana";
	int a = 10;

	//把格式化数据输出到文件中
	fprintf(pa, "%s %d", arr, a);

	fclose(pa);
	pa == NULL;

	return 0;
}

 sprintf

sprintf文档链接:

        

sprintf - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/sprintf/?kw=sprintf        

sprintf:把格式化的数据转化成字符串

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

函数功能是把格式化的数据写入某个字符串中,即发送格式化输出到 str 所指向的字符串 

返回值:

        

如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符

        

如果失败,则返回一个负数

int main()
{
	char arr[] = "kianachuji";
	int num = 20;
	char arr1[20] = { 0 };

	sprintf(arr1, "%s %d", arr, num);//把格式化的数据存放到arr1中
	puts(arr1);//打印arr1

	return 0;
}

4.5 fread和fwrite

fread

fread文档链接:

        fread - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/fread/?kw=fread        

如果我们想要读取多行内容,就可以使用fread函数

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

函数功能从给定输入流stream读取最多count个对象存储到指针ptr指向的数组中,每个对象size个字节,该函数以二进制形式对文件进行操作,不局限于文本文件

返回值

        

返回成功读取的对象个数,若出现错误或到达文件末尾,则可能小于count

        

若size或count为零,则fread返回零且不进行其他动作

        

fread不区分文件尾和错误,因此调用者必须用feof和ferror才能判断发生了什么

fwrite

fwrite文档链接:

        

fwrite - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/fwrite/?kw=fwrite

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

函数功能把ptr指向的数组中的数据存储count个size字节的数据到输出流stream中,该函数以二进制形式对文件进行操作,不局限于文本文件

返回值:

        

如果成功,该函数返回一个 size_t 对象,表示元素的总数,该对象是一个整型数据类型

        

如果该数字与num参数不同,则会显示一个错误

int main()
{
	FILE* pa = fopen("1.txt", "wb");//以输出二进制数据打开文件
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

	//输入5个整型放在文件流中
	fwrite(arr, sizeof(arr), 5, pa);

	fclose(pa);
	pa == NULL;

	FILE* pb = fopen("1.txt", "rb");//以读取二进制数据打开文件
	int arr1[10] = { 0 };

	//从文件中读取三个整形存储到arr1中
	fread(arr1, 4, 3, pb);
	for (int i = 0; i < 10; i++)
	{
		printf("%d", arr1[i]);
	}

	fclose(pb);
	pb == NULL;

	return 0;
}


5. 文件的随机读写

文件的随机读写就是我们可以指定这个文件指针指向的位置,即指向第几个字符,然后从这个字符开始读写

fseek

fseek文档链接:

        

fseek - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/fseek/?kw=fseek

int fseek ( FILE * stream, long int offset, int origin );
                                   
stream:文件指针  

offset:相对于起始位置的偏移量

origin:起始位置

函数功能:根据文件指针的位置和偏移量来定位文件指针

        

返回值:成功返回0,失败返回一个非零值

int main()
{
	FILE* pa = fopen("1.txt", "w");//写
	if (pa == NULL)
	{
		perror("fopen");
		return 1;
	}

	//把字符串输出到文件流中
	fputs("abcdefgh", pa);
	//把光标移到起始位置向右偏移量为5的位置
	fseek(pa, 5, SEEK_SET);
	//把字符串123输出到文件流中
	fputs("123", pa);

	fclose(pa);
	pa == NULL;

	return 0;
}

ftell

ftell文档链接:

        

ftell - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/ftell/?kw=ftell

long int ftell ( FILE * stream );

函数功能:返回文件指针相对于起始位置的偏移量

        

返回值:成功则返回一个偏移量,失败则返回-1

int main()
{
	FILE* pa = fopen("text.txt", "w");//写
	if (pa == NULL)
	{
		perror("fopen");
		return 1;
	}

	//把字符串输出到文件流中
	fputs("abcdefgh", pa);
	//把光标移到起始位置向右偏移量为5的位置
	fseek(pa, 5, SEEK_SET);
	int a = ftell(pa);
	printf("%d", a);

	fclose(pa);
	pa == NULL;

	return 0;
}

rewind

rewind文档链接:

        

rewind - C++ Referencehttps://legacy.cplusplus.com/reference/cstdio/rewind/?kw=rewind

void rewind ( FILE * stream );

函数功能:让文件指针的位置回到文件的起始位置

int main()
{
	FILE* pa = fopen("1.txt", "w");//写
	if (pa == NULL)
	{
		perror("fopen");
		return 1;
	}

	//把字符串输出到文件流中
	fputs("abcdefgh", pa);
	//把光标移到起始位置向右偏移量为5的位置
	fseek(pa, 5, SEEK_SET);
	int a = ftell(pa);
	printf("重置之前偏移量为%d\n", a);

	rewind(pa);//把文件指针重置到文件起始位置
	int b = ftell(pa);
	printf("重置之后偏移量%d", b);

	fclose(pa);
	pa == NULL;

	return 0;
}


6. 文件读取结束的判定

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

 

feof 的作用是:当文件读取结束之后,判断是读取结束的原因是否是遇到文件尾结束

文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )

        

例如:

        

fgetc 判断是否为 EOF                fgets 判断返回值是否为 NULL 

文本文件举例:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
	int c; // 注意:int,⾮char,要求处理EOF
	FILE* fp = fopen("1.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);
}

二进制文件的读取结束判断

二进制文件的读取结束判断,判断返回值是否小于实际要读的个数,例如:

        

fread判断返回值是否小于实际要读的个数,若出现错误或到达文件末尾,则可能小于count,write判断返回值是否等于实际要写入的数据个数

#include<stdio.h>

int main()
{
	FILE* pa = fopen("1.txt", "wb");//以输出二进制数据打开文件
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int ret = fwrite(arr, sizeof(arr), 5, pa);//输入5个整型放在文件流中
	if (ret != 5)//判断是否写入成功
	{
		perror("fwrite");
	}

	fclose(pa);//关闭文件
	pa == NULL;

	FILE* pb = fopen("text.txt", "rb");//以读取二进制数据打开文件
	int arr1[10] = { 0 };
	int rat = fread(arr1, 4, 3, pb);//从文件中读取三个整形存储到arr1中
	if (rat == 3)//判断返回值是否等于size
	{
		printf("返回值等于size\n");
	}
	else//不等于的话判断1.到达文件末尾2.读取出错
	{
		if (feof(pb))
		{
			printf("到达文件末尾");
		}
		else
			perror("fread error");
	}

	for (int i = 0; i < 10; i++)//输入
	{
		printf("%d", arr1[i]);
	}

	fclose(pb);
	pb == NULL;

	return 0;
}


7. 文件缓冲区

ANSIC标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲⽂件系统是指系统自动地在内存中为 程序中每⼀个正在使用的文件开辟⼀块“文件缓冲区”,从内存向磁盘输出数据会先送到内存中的缓 冲区,装满缓冲区后才⼀起送到磁盘上

        

如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输 入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓 冲区的大小根据C编译系统决定的

#include <stdio.h>
#include <windows.h>

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;
}

 


 

完结撒花~
 

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐