【C进阶】文件操作1

 序言:

Hello~大家好,今天给大家带来C语言中有关文件操作的内容,本章内容可能会有点抽象,如果一次性理解不了,可以先收藏下次再看~

本章内容重点:

  • 为什么使用文件?
  • 什么是文件
  • 文件的打开和关闭
  • 文件的顺序读写

目录

为什么使用文件?

什么是文件?

程序文件

数据文件

文件名

文件的打开和关闭

文件指针

 文件的打开和关闭

fopen 

 fclose

文件的顺序读写

流是什么?

流的分类

文件顺序读写的函数

​编辑

sscanf

 sprintf

总结


为什么使用文件?

在一开始,我们通过结构体完成了一个静态通讯录

syseptember的个人博客:C语言通讯录应用程序:从设计到实现

然后我们学习了动态内存管理,实现了动态通讯录

syseptember的个人博客:动态内存管理+动态通讯录

但是改进之后仍然存在一个问题:

每次程序退出后之前存放的通讯录信息不存在了,这是因为通讯录的信息存储在内存中,每一次程序结束后,内存中的信息会被清空,想要信息不被清空,我们就需要将信息存储在文件中

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

什么是文件?

计算机文件,是存储在某种长期储存设备或临时存储设备中的一段数据流,并且归属于计算机文件系统管理之下。所谓“长期储存设备”一般指磁盘、光盘、磁带等。而“短期存储设备”一般指计算机内存

程序文件

源程序文件(后缀.c等),目标文件(Windows环境下后缀为.obj),可执行程序(Windows环境下为.exe),预处理文件(.i),汇编文件(.b)

数据文件

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

本章讨论的是数据文件  :研究如何对数据文件进行读/写

文件名

一个文件要有唯一的文件表示,以便用户识别和引用。

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

例如:c:\code\test.txt

为了方便起见,文件表示常被称为文件名


文件的打开和关闭

文件指针

当我们使用代码对文件进行操作时,每一个被使用的文件都在内存开辟了一个相应的文件信息区,用来存放文件的相关信息(文件名、文件的状态、文件当前的位置等),注意:文件信息区存放的不是文件中的内容,但是可以通过文件信息区维护该文件。文件信息区本质上是一个结构体变量。结构体类型是由系统定义的,取名FILE

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

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

不同的C编译器FILE类型包含的内容不完全一样,但是大同小异

每当打开一个文件时,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息

一般都是通过文件指针来维护这个FILE结构的变量,这样使用起来更加方便

下面我们可以创建一个FILE*的指针变量

FILE* pf//文件指针变量

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

 文件的打开和关闭

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

  • 当我们打开文件时,会自动创建一个文件信息区,此时我们可以定义一个文件指针指向创建的文件信息区,打开文件时,会返回一个FILE* 的指针指向开辟的文件信息区。这有点像动态开辟时伴随着内存中会创建一段内存空间,返回这段空间的起始地址。
  • 在文件操作完之后我们应该关闭文件,释放文件信息区,这有点像动态开辟结束后需要释放掉动态开辟的空间

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

#inlcude <stdio.h>
int main()
{
    //打开文件
    FILE* pFile = fopen("test.c", "w");
    //关闭文件
    fclose(pFile);
    return 0;

fopen 

  • 打开名称在参数文件名中指定的文件,并将其与一个流相关联,该流可以在以后的操作中由返回的 FILE 指针标识。 
  • 流上允许的操作以及这些操作的执行方式由模式参数定义。
  • 如果已知返回的流不是指交互设备(请参阅 setbuf),则返回的流默认是完全缓冲的。
  • 返回的指针可以通过调用 fclose 或 freopen 解除与文件的关联。所有打开的文件都会在程序正常终止时自动关闭。

  • “r”:当指定文件不存在时会报错 
  •  "w":为输出操作创建一个空文件。如果已存在同名文件,则丢弃其内容并将该文件视为新的空文件。
  • "a":打开文件以在文件末尾输出。输出操作总是在文件末尾写入数据,扩展它。忽略重新定位操作(fseek、fsetpos、rewind)。如果文件不存在,则创建该文件。

注意:上述的打开模式针对文本文件,为了将文件作为二进制文件打开,“b”字符必须包含在模式字符串中。这个额外的“b”字符可以附加在字符串的末尾(从而产生以下复合模式:rb", "wb", "ab", "r+b", "w+b", "a+b") 或插入字母和混合模式的 "+" 符号之间("rb+" , "wb+", "ab+").

  • 如果文件成功打开,该函数将返回一个指向 FILE 对象的指针,该对象可用于在未来的操作中识别流。 否则,返回空指针。

fclose

  • 关闭与流关​​联的文件并取消关联 
  • 与该流相关联的所有内部缓冲区都与它解除关联并刷新:写入任何未写入的输出缓冲区的内容,并丢弃任何未读的输入缓冲区的内容

文件的顺序读写

在正式的介绍文件的读写时,我们需要先认识流的概念

流是什么?

计算机中可以将流理解为水流,流是一种抽象的概念,可以把流看作是一种数据载体,通过流可以实现数据交换和传输。 

输入数据相当于从水流中取水,输出数据相当于像水流中倒水

注意:这里所说的输出输出是针对内存而言,如果内存中得到了数据,我们称之为输入数据,如果内存数据给出去了,我们称之为输出数据 。

这么说可能还是有点抽象,我们具体来说:

对于下面这段代码

int main()
{
	int a;
	scanf("%d", &a);//
	printf("%d", a);
	return 0;
}

从流的角度来理解:

变量a存放在内存中,所以当遇见scanf时内存需要从键盘中读取数据,这个流就是我们所说的键盘,键盘是标准输入流

当遇见printf时需要将内存中的数据a输出到流中,这个流就是我们所说的屏幕,屏幕是标准输出流

现在请明白一个点,我们后续要讨论关于文件的函数名是针对内存而言的

流的分类

流可以分为三类

  • 输出流
  • 输入流
  • 错误流

只要一个流可以从内存中接受数据,它就算是输出流,只要一个流可以像内存中传递数据,它就算是输入流。

文件既可以写,也可以读,所以文件即使输入流也是输出流。

 注意:当一个C语言程序进行时,默认会打开标准输出流、标准输出流、标准错误流(scanf、pruntf默认是像这些流读取数据)

文件顺序读写的函数

  •  返回指定流的内部文件位置指示器当前指向的字符。然后内部文件位置指示器前进到下一个字符。
  • 如果流在调用时位于文件末尾,则该函数返回 EOF 并设置流的文件末尾指示符 (feof)。
  • 如果发生读取错误,函数返回 EOF 并设置流的错误指示符 (ferror)。
  • fgetc 和 getc 是等价的,除了 getc 可能在某些库中实现为宏。
  • 函数的返回值设置为int类型为了适应返回失败后EOF的值

  • 向流中写入一个字符并推进位置指示器。
  • 字符写在流的内部位置指示器指示的位置,然后自动前进一个位置
  • 成功时,返回写入的字符。
  • 如果发生写入错误,则返回 EOF 并设置错误指示符 (ferror)。

  • 从流中读取字符并将它们作为 C 字符串存储到 str 中,直到读取 (num-1) 个字符或到达换行符或end of file,以先发生者为准。
  •  换行符使 fgets 停止读取,但函数认为它是有效字符并包含在复制到 str 的字符串中。
  • 在复制到 str 的字符之后自动附加终止空字符。
  • fgets 与 gets 不同点:fgets 不仅接受流参数,而且还允许指定 str 的最大大小,并在字符串中包含任何结束换行符。
  • 如果在尝试读取字符时遇到文件结尾,则会设置 eof 指示符 (feof)。如果这发生在可以读取任何字符之前,则返回的指针是一个空指针(并且 str 的内容保持不变)。
  • 成功输出返回指向str的指针
  • 如果发生读取错误,则设置错误指示符(ferror)并返回空指针(但 str 指向的内容可能已更改)。

  • 函数从str指向的位置复制字符到stream所指向的位置直到遇见了结束符'\0',结束符不会被复制到stream流里
  • fputs与puts不同:fputs需要指定参数流,而且fputs不会像流中添加额外的字符,但是puts会添加一个换行符
  • 如果成功输出,返回非负数
  • 如果输出失败,返回EOF

  • 函数的一些特性和scanf很像,这里不在赘述 

 

  • fprintf和printf类似 

  • 从流中读取 count 个元素的数组,每个元素的大小为 size 个字节,并将它们存储在 ptr 指定的内存块中。
  • 流的位置指示符按读取的字节总数提前。
  • 如果成功,读取的总字节数是 (size*count)
  • 如果此数字与计数参数不同,则表明发生读取错误或读取时已到达文件末尾。在这两种情况下,都设置了正确的指示符,可以分别使用 ferror 和 feof 检查。
  • 如果 size 或 count 为零,则函数返回零并且流状态和 ptr 指向的内容都保持不变。

  • 从 ptr 指向的内存块到流中的当前位置写入一个包含 count 个元素的数组,每个元素的大小为 size 字节。
  • 流的位置指示符按写入的字节总数提前。
  • 在内部,函数将 ptr 指向的块解释为 unsigned char 类型的 (size*count) 个元素的数组,并将它们按顺序写入流,就像为每个字节调用 fputc 一样。 
  • 如果成功,写入的总字节数是 (size*count)
  • 如果此数字与计数参数不同,则写入错误会阻止函数完成。在
  • 情况下,将为流设置错误指示器 (ferror)。
  • 如果 size 或 count 为零,则函数返回零并且流状态和 ptr 指向的内容都保持不变。

 注意:若只读文件则请使用r/rb,若只写文件请使用w/wb

sscanf

sscanf是针对字符串格式化输入的函数(若想要输入整数,则字符串第一个非空白字符应该是整形,并且一直读取到第一份非整形字符,类似于atoi)

sprintf

sprintf是针对字符串的格式化输出函数

总结

现在来总结一下本章所涉及到的知识点:

  1. 使用文件可以将数据长期保存起来,使数据持久化
  2. 文件分为程序文件和数据文件。程序文件:源程序文件、目标文件、可执行文件。数据文件:存放程序运行时的数据的文件,比如程序需要读取数据的问价或者输出内容的文件
  3. 文件名分为3部分:文件路径、文件名主干、文件后缀
  4. 每个被使用的文件都在内存中开辟了对应的文件信息区(FILE对象),我们通过文件指针找到文件信息区,并且通过文件信息区来为对文件进行维护(读、写)
  5. 读写文件前应该打开文件、文件使用完后应该关闭文件,fopen打开文件、fclose关闭文件。文件有多种打开方式,当打开方式为"w"、"wb"时,只可以对文件进行写入,写入的文件如果有重名、会先销毁重名文件再写入新内容;打开方式"r"、"rb",只可以对文件进行读取,读取的文件必须存在
  6. 计算机中流是一种数据的载体,内存可以对流中的数据进行输入、输出
  7. 文件的顺序读写有多种函数

 

猜你喜欢

转载自blog.csdn.net/m0_74278159/article/details/129858968
今日推荐