C++——文件操作的讲解

文件的分类

文件我们都已经熟知,但在程序设计的时候文件分为两种,一种是程序文件,一种是数据文件(从文件的功能来分类)。

程序文件:

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

数据文件:

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

但是这篇文章主要讨论数据文件

文件名

⼀个文件要有⼀个唯⼀的文件标识,以便用户识别和引用。 文件名包含3部分:文件路径+文件名主干+文件后缀 ,例如: c:\code\test.txt 为了方便起见,文件标识常被称为文件名。

数据文件的两种形式:

根据数据的组织形式,数据文件被称为文本文件或者⼆进制文件

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

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

流的介绍及其包含的内容:

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

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

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

2. stdout-标准输出流,大多数的环境中输出至显示器界面,printf函数就是将信息输出到标准输出 流中。

3.stderr-标准错误流,大多数环境中输出到显示器界面。

 这是默认打开了这三个流,我们使用scanf、printf等函数就可以直接进行输入输出操作的。 stdin、stdout、stderr三个流 的类型是: FILE* ,通常称为文件指针。 C语言中,就是通过 FILE* 的文件指针来维护流的各种操作的。

文件的打开和关闭:

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

 这里有一个笼统的文件模版:

//打开⽂件 
FILE * fopen ( const char * filename, const char * mode );
//关闭⽂件 
int fclose ( FILE * stream );

下面是文件的打开模式: 

下面是一个小例子(便于理解):

#include <stdio.h>
int main ()
{
//设置文件指针变量
 FILE*pf;
//打开文件
 pf=fopen("test.c","w");
//判断文件是否建立,防止野指针的出现
if(pf==NULL)
{
//用perror来判断错误的原因
  perror("pf");
//如果错误退出程序
  return 1;
}
  fclose(pf);
//关闭文件时,不要忘了将指针变为空,防止野指针的出现
pf=NULL;
   return 0;
}

 读写函数的介绍:

 (在这里的输出是指将数据写入文件内,输入是指将数据从文件内写到程序上)

fputc与fgetc的介绍:

通过cplusplus的搜索可以看到fputc的用法,前面我们要输入一个字符,第二个我们要输入一个流。

以下便是一个例子:

在这里我输入完后进行了编译,出现我这样的情况,编译通过,那么他便创建了一个文件叫test.c

 打开文件:

我们可以看到这里面有我刚刚传入的字符a。

(公司是翻译器的问题,没有公司二字)

我们可以看到fgetc的定义是int的类型,那么当他输入时会返回整数。 

我通过循环来输出文件里面的值。

下面是我们在开始的时候容易犯的错误:

这种编译后就没有出现我们文件中的值,这是因为我们在打开文件的时候,是按照只写("w")的方式打开,所以就不会出现我们的结果。

在这里我又新开了一个文件叫tests.c,当我用只读的方式来写时,即使我有fputc,他也是错误的,因为他不会帮你开辟这个文件。所以会报错,告诉你没有这个文件(这个是perror的功能) 。

fputs与fguts的介绍:

fputs与fputc的区别就是一个输入字符串,一个输入字符。

所以你要输入一个abcdef时,可以一步到位

那么我想输入两段字符串,但是我又不想让他们在同一行,这怎么办?

下面便是解决方法;

fgets有三个变量,第一个是从你所指定的流中读取到的数据的拷贝,第二个是你让我读取多少个数据,第三个是流。(但要注意的是用fgets,如果你要他读取10个,他只会读取9个,最后一个放置\0,这便是第一行为什么他会读取num-1的原因)

那么我怎样来读取两行的数据呢?

 我们可以通过循环来完成,我们可以知道fgets是char*类型的,所以当我读取时,只要不是空指针,便会一直读取下去。

fprintf与fscanf的介绍:

接下来我们通过对比的形式来看fprintf 

 

我们可以看到,他与printf就相差一个流,那么我们可以先按照printf的形式来写,最后加上这个流即可 。

下面是一个例子

 

我们看到流的地方只需要写上你要存入的文件指针即可,其他的地方与printf没有区别。

 与fprintf类似,fscanf与scanf也只差一个流。它是将文件中的数据写到程序中。

下面是一个举例:

 我从文件中,拿出了其中的数据,然后给了一个新定义的结构体。

fwrite与fread的介绍:

 

fwrite是二进制的输出函数。第一部分是你要拷贝的内容,第二部分是拷贝文件的每一个大小,第三个是你要拷贝的个数,第四部分便是流(你要把这些拷贝到哪里去)。

下面是一个举例:

 

sizeof(arr)是数组arr的整体大小,sizeof(arr[0])求的是数组一个是多大,两个相除便可以求出数组一共有多少个。 

我们可以看到用二进制写的代码,他的内容我们是看不懂的,所以我们要用特殊的方法 。

 

下面是fread的介绍,我们可以看到,fread与fwrite的格式是一样的。它是将文件中的数据拷贝到你的第一部分。

下面是一个例子 :

我们可以看到他成功打印出了我们文件的内容。

但是这是我们在知道文件有多少个的情况下打印的,如果我们不知道有多少个呢?

 

这是fread函数介绍的一部分,我们可以根据这一性质来进行输出 

我们每次只读取一个,如果读取成功则返回的值为1,那么就会拷贝到a【i】里面去,我们在输出数组a的内容,如果遇到结尾,则fread的返回值为0,说明拷贝完成。

 

文件的随机读写:

1.fseek:

指针的偏移

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

int origin 的位置可以填写

SEEK_SET(文件指针起始位置)   SEEK_CUR(文件指针当前位置) 

SEEK_END(文件指针末尾) 

 第二部分可以填写负数,指向移动。

​
include<stdio.h>
int main ()
{
 FILE * pFile;
 pFile = fopen ( "example.txt" , "wb" );
 fputs ( "This is an apple." , pFile );
 fseek ( pFile , 9 , SEEK_SET );
 fputs ( " sam" , pFile );
 fclose ( pFile );
 return 0;
}

​

2.ftell

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

long int ftell ( FILE * stream );

 以下是一个例子:

#include <stdio.h>
int main ()
{
 FILE * pFile;
 long size;
 pFile = fopen ("myfile.txt","rb");
 if (pFile==NULL) 
 perror ("Error opening file");
 else
 {
 fseek (pFile, 0, SEEK_END); // non-portable
 size=ftell (pFile);
 fclose (pFile);
 printf ("Size of myfile.txt: %ld bytes.\n",size);
 }
 return 0;
}

 3.rewind

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

void rewind ( FILE * stream );

 文件读取结束的判定:

 1.feof:

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

int feof ( FILE * stream );

文件结束了,如果为非零值则文件正常读取结束(读取到文件末尾)。如果结果为0,则运算过程中有错误

2.ferror

 检查是否发生错误而结束

int ferror ( FILE * stream );

 如果发生错误结束则返回非零的数,否则为

 

​
#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)!=0)
 puts("I/O error when reading");
 else if (feof(fp)!=0)
 puts("End of file reached successfully");
 
 fclose(fp);
}

​

 文件缓冲区:

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

 

以下是一个小例子来说明缓冲区的存在 

#include <stdio.h>
#include <windows.h>
//VS2019 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语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。 如果不做,可能导致读写文件的问题。  

 

 

 

 

 

 

 

 

  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值