c语言文件操作

为什么要使用文件

在程序运行的时候,所有的数据都是保存在内存中的,比如说变量,数组。而如果程序结束,这些数据就会消失,下一次打开程序就不会存在了。为了让数据能够持久化,我们可以让数据存在磁盘文件里。

把数据存到电脑的硬盘上,就能实现数据持久化。

什么是文件

程序的文件分为两大类:程序文件,数据文件

程序文件

程序文件包括源文件(.c),目标文件(windows系统下是.obj),可执行文件(windows环境下 .exe)等等

 数据文件

被用来存放数据,一般程序读取或者写入数据的地方。

文件名

一个文件名要有唯一的文件表示:

文件路径+文件名主干+文件后缀

例如:c:\code\test.txt

文件的打开和使用

文件指针

文件本来在硬盘中,当我们要对文件进行操作的时候,先要打开文件。这里打开文件就是在内存区域创建一个临时空间,被称为文件信息区,在这个空间中放入文件的各种信息(路径,名字,状态),而这些信息是保存在一个结构体变量中的。这个结构体变量的类型是FILE。(这个结构体的声明是在头文件stdio里面的,结构体包含了文件的各种信息)。而指向这个空间的结构体指针,就是文件指针----FILE*pf

(FILE这个类型是把结构体类型重命名后的,像这样  typedef struct A  FILE ;   ,在头文件里是这样重命名的)

所以在打开文件时,系统会自动创建一个空间,并把文件信息放到空间里的变量里。

我们可以创建这样一个指针,就能通过这个指针来找到与它关联的文件。

FILE * pf;

 文件的打开和关闭

这里的打开文件不是简单的我们电脑上双击文件,然后把文件的页面打开。

打开文件后,会在内存中创建一个文件信息区,这个时候会返回一个FILE* 类型的文件信息区的指针,这样就能建立指针和文件信息区间的联系。

因为文件打开后会占用内存的空间, 所以对文件操作完之后,要关闭文件。

规定的文件打开和关闭函数是:fopen  ,   fclose

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

两个参数,

第一个filename是文件的路径,别忘了用""

第二个mode是文件的打开方式(文件的使用方式),也要用""

fclose
int fclose ( FILE* stream);

stream就是要关闭的文件的指针 。

返回值:如果关闭成功了,返回0;如果关闭失败,返回EOF

文件的打开方式

如下

!!如果指定文件存在,那些要”建立一个新文件”的文件使用方式,会建一个新文件并覆盖掉原有的内容。!!

使用方式

int main()
{
    FILE * pf;
    pf = fopen("C:\code\test.txt","w");//以写的方式打开文件
    if(pf == NULL)
    {
         perror("错误是:");
    }  
    else
    {
    //文件操作.....    
    }
    
    fclose(pf);//关闭文件
    pf  ==  NULL  
    return 0;
}

 

文件的顺序读写

什么是读/写,输入/输出

这些都是以内存为主体,要站在内存的角度思考

键盘的信息,内存区读取,并且输入数据进来

内存中的信息,写(输出)到屏幕,是输出数据出去。

对文件,把数据传给文件,是输出,写

内存从文件里拿数据,是输入,读 。

字符相关读写函数

fgetc
int fgetc ( FILE * stream )

读取目标地址的文件,按顺序读取文件里的内容

读取成功,返回文件指针正在指向的那个字符的ASCII码值。然后把指针向后移动一位(下一次调用就会读取下一个字符了)

如果读取失败,返回EOF

int ch = fgetc(pf);
printf("%c\n",ch);
fputc
int fputc ( int character, FILE * stream )

在文件指针处写下一个字符 character,一次写一个,按顺序一个一个写(是int因为传进来的是ASCII码值)

如果写文件失败,返回EOF。

fputc('a',pf);

 文本行(字符串)相关读写函数

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

 str读取到的字符串要放到内存中的位置的指针

num是读取并拷贝到str里的最大字符数。

char buf[20]={0};//创建一个用来存放读取到的内容的数组
fgets(buf,5,pf);//读取内容存放到buf内,读取5个字符
//实际上读4个,因为在buf内最后要放\0
//读完buf内有5个元素
printf("%s\n",buf);//打印buf

一次只能读一行,读取字符数超过文件中一行的字符数,超出的不会读到下一行,而是直接不读了。 

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

str指向字符串,stream指向文件

fputs("abcdef",pf);

在文件里写下abcdef

格式化的读写函数

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

从文件里输入信息到内存中

看看熟悉的scanf

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

可以发现,fscanf 比 scanf就多了个文件指针。由此可以得出,fscanf和scanf的使用方法是一样的 

scanf是从我们键盘输入的缓冲区读取数据放到内存中对应的地方,fscanf是从文件中读取数据放到内存中对应的地方

int a;
char b;

fscanf(pf,"%d %s",&a,&d);//把文件中的内容输入到a,b中
//可以把文件中的内容,看作是用scanf时,缓冲区的内容

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

同上,使用操作适合printf是一样的,只不过printf是把数据打印到屏幕上,而fprintf是把数据输入到文件里。

int a=5;
char b='m';
fprintf(pf,"%d,%s\n",a,b);

什么是流

在c语言中,数据传到输出设备时,不是直接传过去的,而是先传到 里,之后再由c语言封装好,传给设备。

流,是由FILE*的指针来管理的

任何一个c语言程序,在打开和运行时,默认会打开三个流:

stdin---标准输入(键盘)   类型:FILE*

stdout--标准输出(屏幕)             FILE*

stderr--标准错误(屏幕)             FILE*

所以像是printf,scanf能直接用,且起效果,不用我们自己去打开流。

而上面的每个函数,适用于每个输入/输出流

-->既可以从键盘获取数据,也可以从文件中获取/既可以输出到文件,也能输出到屏幕。

int main()
{
    int ch = fgetc(stdin);//从键盘输入流获取
    fputc(ch,stdout);//在屏幕上输出
    return 0;
}
struct S
{
    char name[20];
    int age;
    float score;
};

int main()
{
    struct S s = {0};
    fscanf(stdin,"%s %d %f",s.name,&(s.age),&(s.score));//从键盘读
    fprintf(stdout,"%s %d %f",s.mane,s.age,s.score);//输出到屏幕

    return 0;
}

二进制输入输出函数

进行二进制的读写,在打开文件时要用二进制的形式打开,参考前面文件打开方式

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

ptr,元素的地址

size,元素的大小

count,元素的个数

stream,写文件的地址指针

struct S 
{
    char name[20];
    int age;
    float score;
};
int main()
{
    struct S s={"张三",20,98.5};
    FILE *pf =fopen("test.txt","wb");//打开一个二进制文件
    if(NULL==pf)
    {
        perror("fopen");
        return 1;    
    }
    
    //写文件
    fwrite(&s, sizeof(struct S) , 1, pf);
    
    //关闭文件
    fclose(pf);
    pf=NULL;
}

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

ptr,用与存放读到的数据

size,读取数据元素的大小

count,元素个数

stream,读取地址 

struct S 
{
    char name[20];
    int age;
    float score;
};

int main()
{
    struct S s={0};
    FILE *pf =fopen("test.txt","rb");//注意!!
//!!这里是以读的形式打开二进制文件
    if(NULL==pf)
    {
        perror("fopen");
        return 1;    
    }
    
    //读文件
    fread(&s, sizeof(struct S) , 1, pf);
    printf(%s %d %f\n),s.name,s.age,s.score);
    
    //关闭文件
    fclose(pf);
    pf=NULL;
}

类似的函数

sprintf---把一个格式化的数据,存放在字符串中。

sscanf--从一个字符串中还原出一个结构体(格式化的数据)。

int main()
{
	struct S s = {"zhangsan", 20, 98.5};
	char buf[100] = { 0 };
	sprintf(buf, "%s %d %f", s.name, s.age, s.score);
	printf("%s\n", buf);//按照字符串打印的

	struct S tmp = { 0 };
	sscanf(buf, "%s %d %f", tmp.name, &(tmp.age), &(tmp.score));
	printf("%s %d %f\n", tmp.name, tmp.age, tmp.score);//打印结构体数据

	return 0;
}

文件的随机读写

前面的是文件的顺序读写,只能按顺序下去读或写,文件的随机读写就是能从文件的任何地方开始读或写。

fseek

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

根据文件指针位置和所给的偏移量来找到读写位置。

offset是偏移量

origin:1.SEEK_SET---文件指针起始位置

          2.SEEK_CUR---文件指针当前位置

          3.SEEK_END---文件指针结尾位置

fseek只要作用,就是调整文件指针

fseek( pf , 1 , SEEK_CUR);

就是把指针调整到当前位置再往后便宜一个位置。

ftell

long int ftell(FILE * stream);

得到当前位置相对于起始位置的偏移量

ftell(pf);

rewind

void rewind(FILE*stream);

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

rewind(pf);

使用实例

//假定test.txt的内容是:abcdef
int main()
{
	FILE* pf = fopen("test.txt","r");
	if (pf == NULL)
	{
		perror("fopen");
	}
	else
	{
		int ch = fgetc(pf);
		printf("%c\n", ch);//a,读取一次后,光标往后移动一位,指针向后移动一位
		ch = fgetc(pf);//第二次读取时光标在第二位
		printf("%c\n", ch);//b

		fseek(pf, -1, SEEK_CUR);//调整光标位置,SEEK_CUR是当前的指向,是c
		ch = fgetc(pf);
		printf("%c\n", ch);

		printf("%d\n", ftell(pf));//2

		rewind(pf);//文件指针位置回到初始位置
        //必须要回到初始位置才能关闭文件,不然找不到要关闭文件的位置
		ch = fgetc(pf);
		printf("%c\n", ch);

	}
	return 0;

文本文件和二进制文件

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

假设有一个10000要存到磁盘中

 文件读取结束的判定

feof/ferror

feof(pf)

ferror(pf)

在文件读取的过程中,不能用feof函数的返回值来直接判断文件是否结束,feof是用来判断文件读取结束的原因--->是不是因遇到eof才结束的(EOF是文件结束的标志)

正确流程是:

首先文件读取结束了

结束后想知道读取结果:

feof(pf)---返回值为真,就说明是文件正常读取遇到了结束标志而结束的

ferror(pf)---返回值为真,就说明是文件在读取过程中出错了而结束的。

  • 32
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值