C语言之文件操作

目录

为什么使用文件

文件名

文件指针

流 

文件的打开和关闭

前言

文件的打开方式

文件打开关闭函数

fopen函数

fclose函数

文件的顺序读写

fputc函数

fgetc函数

fputs函数

fgets函数

fprintf函数

fscanf函数

fwrite函数

fread函数

文件的随机读写

fseek函数

ftell函数

rewind函数

文本文件和二进制文件

一个数据在内存中怎么储存

文件读取结束的判定

feof函数

文件缓冲区

理解:

fflush函数

为什么使用文件

使用文件我们可以将数据直接存放在电脑硬盘上,做到了数据的持久化

在程序设计中,我们一涉及的两种文件

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

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

注意:本章讨论的主要是数据文件

文件名

定义:一个文件有一个唯一的文件标识,以便用户识别和引用

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

eg:c:\code\test,txt

文件指针

定义:缓冲文件系统中,关键的概念是文件类型指针,简称文件指针。

关于FILE

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

关于VS2013编译环境提供的stdio.h头文件中有以下的文件类型申明

typedef struct _iobuf {
	char* _ptr;         //文件输出的下一个位置
	int _cut;			//当前缓冲区的相对位置
	char* _base;		//指基础位置,文件的起始位置
	int _flag;			//文件标志
	int _file;			//文件有效性检查
	int _charbuf;		//检查缓冲区状况,如果无缓冲区则不读取
	int _bufsiz;		//文件的大小
	char* _tmpfname;	//临时文件名
}FILE;

注意:

  • 不同C编译器的FILE类型包含的内容不完全相同,但是大同小异
  • 每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构类型的变量,并填充其中的信息,使用者不必关心细节
  • 一般都是通过一个FILE指针来维护这个结构的变量,这样使用起来更加方便        

创建一个FILE* 的指针变量

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

定义的pf是一个指向FILE类型数据的指针变量,可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。

通过文件指针变量可以找到与他相关联的文件 

C语言之文件操作_第1张图片

流 

流:程序数据可能要操作各种各样的硬件(如:硬盘、光盘、u盘等)硬件的不同导致了其读写方式的不同,因为要记硬件读写方式的成本过高,所以就在中间层抽象一个流(其是一个标准)的概念,此时程序向流里面输入数据,然后流再把相应的数据写到我们的不同设备中,因而我们程序员就不用关心硬件读写方式了。

C语言程序只要运行起来,就默认打开了3个流

  • stdin——标准输入流——对应键盘
  • stdout——标准输出流——对应屏幕
  • stderr——标准错误流——对应屏幕

文件的打开和关闭

前言

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

文件的打开方式

C语言之文件操作_第2张图片

文件打开关闭函数

fopen函数

当用fopen打开一个文件的时候,其会主动创建该文件的文件信息区 ,并且把这个文件信息区的起始地址返回过来,如果打开失败则返回null

参数:filename:传入的文件名        mode:文件的打开方式

fclose函数

注意:使用fclose函数就可以把缓冲区内最后剩余的数据输出到内核缓冲区,并释放文件指针和有关的缓冲区 

传入参数为文件类型的指针 

返回值:如果成功关闭则返回0,否则返回eof(-1) 

使用:

引入头文件:#include

#include 
void main() {
	FILE* pf = fopen("C:\\All\\test.txt", "w");//(绝对路径)打开文件
	if (pf==NULL)
	{
		perror("fopen");
	}
	fclose(pf);//关闭文件
	pf = NULL;
}

文件的顺序读写

注意:内存中的数据输出到硬盘中的过程叫写入,硬盘中的数据输入到内存中的过程叫读取 

C语言之文件操作_第3张图片

fputc函数

写一个字符到一个流里面,具体要写ASCII码值为c的字符,stream是具体写到哪个地址的文件中

当写入成功时返回写入的字符,当写入失败时返回EOF(文件结束标志)

使用:

引入头文件:#include

#include 
void main() {
	FILE* pf = fopen("test.txt", "w");//打开文件
	if (pf==NULL)
	{
		perror("fopen");
	}
	char c[] = "hello world";
	for (int i = 0; i <= 11; i++)
	{
		printf("%c",fputc(c[i], pf));

	}
	fclose(pf);//关闭文件
	pf = NULL;
}//hello world

fgetc函数

在stream文件地址处的文件里读取一个字符,如果读取正常就返回该字符的ASCII码值,如果读取失败或遇到EOF则返回EOF

注意:当打开文件并使用fgetc函数读取文件时,fgetc函数从文件的开始位置读取一个字符,每读取一个字符后文件的位置指针向后移动一个字符的位置。若当前读取的是文本文件,当遇到文件结束时返回值为EOF

使用:

引入头文件:#include

#include 
void main() {
	FILE* pf = fopen("test.txt", "r");//打开文件
	if (pf==NULL)
	{
		perror("fopen");
	}
	//读取文件
	printf("%c", fgetc(pf));//h
	printf("%c", fgetc(pf));//e
	printf("%c", fgetc(pf));//l
	printf("%c", fgetc(pf));//l
	printf("%c", fgetc(pf));//o
	fclose(pf);//关闭文件
	pf = NULL;
}

fputs函数

写一个string字符串到stream文件指针所指向的文件中,如果写入成功则返回非0(默认为1),如果写入错误则返回EOF

使用:

引入头文件:#include

#include 
void main() {
	FILE* pf = fopen("test.txt", "w");//打开文件
	if (pf==NULL)
	{
		perror("fopen");
	}
	//写入文件
	fputs("hi", pf);
	fputs(" world", pf);
	fclose(pf);//关闭文件
	pf = NULL;
}//hi world(写入的是1行如果需要换行则需要在字符串中加'\n')

fgets函数

在stream指针所指向的文件中读取最多n个字符放到string地址所指向的空间中,返回该地址的指针,如果读完或出现错误则返回null

注意:如果n=100,那么读取的实际有99个非'\0'字符,最后一个放'\0'

使用:

引入头文件:#include

#include 
void main() {
	FILE* pf = fopen("test.txt", "r");//打开文件
	if (pf==NULL)
	{
		perror("fopen");
	}
	//读取文件
	char arr[6] = { 0 };
	printf("%s\n", fgets(arr, 6, pf));//hi wo
	printf(arr);//hi wo
	printf("%s\n", fgets(arr, 6, pf));//rld
	printf(arr);//rld
	fclose(pf);//关闭文件
	pf = NULL;
	//由此观之再次读取时用该数组接受时数组数据被覆盖
}

fprintf函数

将agars(参数表)内各项的值,按format(格式控制字符串)所表示的格式,将数据格式为字符串的形式写入到文件指针stream所指向的文件中,fprintf函数返回值是输出的字符数,发生错误时返回一个负值

使用:

引入头文件:#include

#include 
struct Stu
{
	char arr[10];
	int num;
	float sc;
};
void main() {
	FILE* pf = fopen("test.txt", "w");//打开文件
	if (pf==NULL)
	{
		perror("fopen");
	}
	struct Stu s = { "hello",10,5.5f };
	fprintf(pf, "%s %d %f", s.arr, s.num, s.sc);//hello 10 5.500000
	fclose(pf);//关闭文件
	pf = NULL;
}

fscanf函数

将stream指针所指向的文件的数据以format(格式控制字符串)的形式 读取出来,放到argu各种参数中,对于返回值fscanf函数在没有出错的情况下返回实际读取数据的个数,如果出错或者读到结尾则返回EOF

使用:

引入头文件:#include

#include 
struct Stu
{
	char arr[10];
	int num;
	float sc;
};
void main() {
	FILE* pf = fopen("test.txt", "r");//打开文件
	if (pf==NULL)
	{
		perror("fopen");
	}
	struct Stu s = {0};
	fscanf(pf, "%s %d %f", s.arr, &(s.num), &(s.sc));//第一个也可以&(s.arr)没有取址是因为结构体地址与第一个元素地址0偏移
	printf("%s %d %f\n", s.arr, s.num, s.sc);//hello 10 5.500000
	fclose(pf);//关闭文件
	pf = NULL;
}

fwrite函数

 作用:将一块内存区域中的数据(buffer所指向的内存区域)以二进制的形式写入到本地文本(stream指向的文件)其返回值返回的是实际写入到文件的基本单元个数

    -- buffer:指向数据块的指针  
    -- size:每个数据的大小,单位为Byte(例如:sizeof(int)就是4)  
    -- count:数据个数(最多) 
    -- stream:文件指针  

使用:

引入头文件:#include

#include 
struct Stu
{
	char arr[10];
	int num;
	float sc;
};
void main() {
	FILE* pf = fopen("test.txt", "w");//打开文件
	if (pf==NULL)
	{
		perror("fopen");
	}
	struct Stu s = {"hi",10,5.5f};
	//二进制形式写文件
	fwrite(&s, sizeof(struct Stu), 1, pf);
	fclose(pf);//关闭文件
	pf = NULL;
}

fread函数

将stream指针所对应的文件数据读取到buffer指针所对应的内存区域中,size为单个数据的大小,count为数据的个数,该函数的返回值为实际读取到元素的个数,如果发现要读取到完整的元素个数小于指定的元素个数,这就是最后一次读取,读取失败返回0

使用:

引入头文件:#include

#include 
struct Stu
{
	char arr[10];
	int num;
	float sc;
};
void main() {
	FILE* pf = fopen("test.txt", "r");//打开文件
	if (pf==NULL)
	{
		perror("fopen");
	}
	struct Stu s = {0};
	//二进制形式读文件
	fread(&s, sizeof(struct Stu), 1, pf);
	printf("%s %d %f\n", s.arr, s.num, s.sc);//hi 10 5.500000
	fclose(pf);//关闭文件
	pf = NULL;
}

文件的随机读写

fseek函数

根据文件指针的位置和偏移量来定位文件指针(调整文件指针)

参数:

stream——针对stream文件地址所对应的文件

offset——偏移量【字节为单位】(负数为向左偏移,正数为向右偏移)

origin——起始位置(从哪开始偏移)

关于起始位置有3个参数

  • SEEK_SET:文件开头
  • SEEK_CUT:当前位置
  • SEEK_END:文件结尾

返回值:成功则返回0,否则返回其他值

使用:

引入头文件:#include

#include 
void main() {
	FILE* pf = fopen("test.txt", "r");
	if (pf==NULL)
	{
		perror("fopen");
		return;
	}
	//读取文件
	int ch = fgetc(pf);//pf所指向的文件内容为abcdefg
	printf("%c\n", ch);
	//调整文件指针
	fseek(pf, -1, SEEK_CUR);//pf指向的文件,其指针从当前位置向左偏移1字节
	ch = fgetc(pf);
	printf("%c\n", ch);
	ch = fgetc(pf);
	printf("%c\n", ch);
	//关闭文件
	fclose(pf);
	pf = NULL;
}//没加fseek函数时为abc,加了之后为aab
//注意如果遇到换行的情况,说明前一行的字符串末尾有'\0'其也会记在偏移量中

ftell函数

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

rewind函数

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

使用:

引入头文件:#include

#include 
void main() {
	FILE* pf = fopen("test.txt", "r");
	if (pf==NULL)
	{
		perror("fopen");
		return;
	}
	//读取文件
	int ch = fgetc(pf);//pf所指向的文件内容为abcdefg
	printf("%c\n", ch);
	//调整文件指针
	fseek(pf, 1, SEEK_CUR);//pf指向的文件,其指针从当前位置向左偏移1字节
	int a = ftell(pf);//告知文件指针相对于起始位置的偏移量
	printf("%d\n",a);//2
	rewind(pf);//让文件指针的位置回到文件的起始位置
	printf("%d\n",ftell(pf));//0
	//关闭文件
	fclose(pf);
	pf = NULL;
}

文本文件和二进制文件

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

二进制文件:数据在内存中以二进制的形式储存,如果不加转换的输出到外存,就是二进制文件

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

一个数据在内存中怎么储存

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

10000储存方式

二进制:(共占用4字节)

  • int形式:00000000 00000000 00100111 00010000

ASCII形式:(共占5字节)

  • 00110001 00110000 00110000 00110000 00110000

关于ASCII形式:共5字节每个字节都代表一个ASCII码值分别为(1、0、0、0、0)的ASCII码值

文件读取结束的判定

feof函数

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

使用时需引入头文件:#include  

返回值:若达到文件尾部EOF则返回非0;否则返回0

注意:文件结束标志——end of file(EOF——值为-1) 

文件缓冲区

理解:

C语言之文件操作_第4张图片

C语言之文件操作_第5张图片

fflush函数

调用该函数会将缓冲区中的内容写到stream所指向的文件中去(对于内存缓冲区来说是清空,对于硬盘来说是写入),若stream为null,则会将所有打开的文件进行数据更新。

返回值:如果成功刷新则返回0,指定的流没有缓冲区或者只读打开时也返回0,错误返回-1;

使用:

引入头文件:#include

#include 
#include 
void main() {
	FILE* pf = fopen("test.txt", "w");
	fputs("abcdef", pf);//先将代码放在输出缓冲区
	printf("睡眠10s-已经写数据了,发现test.txt里没数据\n");
	Sleep(10000);
	printf("刷新缓冲区\n");
	fflush(pf);//刷新缓冲区,将输出缓冲区的数据写到文件
	printf("睡眠10s再次打开test.txt,有内容了。\n");
	Sleep(10000);
	//关闭文件也会刷新缓冲区,所以才有第二次睡眠,因为要确定刷新缓冲区的作用
	fclose(pf);
	pf = NULL;
}

注意:因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束时关闭文件(文件关闭时也会刷新缓冲区),如果不做可能会导致读写文件的问题。

你可能感兴趣的:(C语言,linq,c#)