关于文件操作

引言:

对于文件操作,我们需要了解为什么要使用文件。我们都知道,一个程序如果执行完毕,那么在这个过程中所产生的数据都会随之回收,也就无法长久的保存起来,那么这时候我们就需要文件来进行持久化的保存,这就说明我们为什么要使用文件了。

一 . 什么是文件

磁盘(硬盘)上的文件即为文件。

在程序设计中,我们通常谈的文件有两种:程序文件和数据文件(根据文件的功能来进行区分)。

1.程序文件

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

2.数据文件

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

这里我们主要讨论的是数据文件。

3.文件名

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

文件名包含三个路径:文件路径+文件名主干+文件后缀

比如:C:\Users\MR\Desktop.txt

为了方便起见,文件标识常被称为文件名。

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

根据数据的组织形式,我们又可以把数据文件分为文本文件二进制文件

简单来说,文本文件一般是我们看得懂的文件,而二进制文件则一般是看不懂的。

1.文本文件

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

2.二进制文件

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

3.举例:

一个数据在文件中存储方式有两种,一般来说字符一律以ASCII码的形式存储,数值型数据既可以用ASCII码形式存储,也可以使用二进制形式存储。

例如我们要存储10000这个整型数据,对于整形数据,我们在文件中有两种储存的方式,第一种为储存ASCII码字符的形式,另一种为直接储存二进制的方式,接下来我会讲解10000在两种方式下被放入文件的方式。

例如下图:

当我们直接以二进制的形式储存进去时,并且使用4个字节的大小空间,存进去的就是10000的补码形式,如上图所示;如果我们以ASCII码的字符形式存进去的话,那么10000会被当成5个字符,而字符1对应的ASCII码值为49,字符0对应的ASCII码值为48,故存进去的为如图上所示。

四 . 文件的打开和关闭

我们程序的数据需要输出到各种外部设备,也需要从外部设备获得我们想要的数据,但是不同的外部设备的输入输出操作是各不相同的,为了方便程序员对各种设备进行方便的操作,此时就抽象出了流的概念,我们可以把流想象成流淌着字符的河水。

1.流和标准流

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

标准流:由上面我们可以知道,我们获取数据和写入数据都要打开流冰对其进行相应的操作,那么我们平常从键盘上输入数据,向屏幕输出数据,却并没有打开流或者关注过流,这时为什么呢?

原因是c语言程序在启动的时候,它会默认给我们打开了3个流:

· stdin—标准输入流,在大多数环境中从键盘输入,scanf就是从标准输入流中读取数据。

· stdout—标准输出流,大多数环境中输出到显示器界面,printf就是将信息输出岛标准输出流中。

· stderr—标准错误流,大多数环境中输出到显示器界面,可以理解为标准输出流的一种。

这三个流是默认打开的,这样我们就可以方便地使用scanf函数和printf函数了。

这三个流stdin,stdout,stderr的类型都为FILE*,称为文件指针

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

2.文件指针

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

当我们在使用文件指针的时候,每个被使用的文件都会在内存中开辟一块空间即文件信息区,用来存放文件的相关信息(如文件名,文件状态以及文件路径等)。这些信息保存在一个结构体变量中的。这个结构体类型是由系统声明的,并取名为FILE。

例如,在vs2013编译环境提供的stdio.h头文件有以下的文件类型的声明:

注意,不同的c编译器的FILE类型包含的内容不完全相同,但都是大同小异的!

当我们打开一个文件时,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的文件信息,使用者无需在意细节方面。

我们一般都是通过FILE的指针来维护这个FILE结构的变量,这样用起来方便。

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

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

下面我将简单演示一下,例如:

当我们打开文件1时,系统会自动创建一个结构体变量用于存放文件的相关信息,也就是FILE结构体类型变量,然后我们通过FILE*指针变量对这个文件信息区进行访问,从而能够找到我们打开的文件1,文件2和文件3也是一样的道理。

3.文件的打开与关闭

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

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

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

fopen:第一个参数为const char * filename,即文件的名字;第二个参数为const char * mode,即打开文件的方式,例如读文件或者写文件;fopen是打开文件的函数,如果打开成功,它会返回一个FILE*的指针,如果打开失败,它会返回空指针(NULL)。

fclose:只有一个FILE* stream参数,意思时关闭stream所指向的流,我们也可以理解为关闭指向的文件;当fclose关闭成功时会返回0,如果关闭失败则会返回EOF(通常为-1)。

注意:使用fopen函数和fclose函数都需要包含头文件#inlcude <stdio.h>

对于文件的打开方式mode,如下图:

对于文件的打开和关闭,我们用下列代码进行演示,如图:

这里我们要对pf进行判断是否打开成功;并且当我们关闭了文件后,pf仍可以指向那个地址的空间,也就是为野指针,故我们最后要给pf赋值为空指针(NULL)。

五 . 文件的顺序读写

1.顺序读写函数

接下来介绍顺序读写函数的使用,顺序读写函数有如下:

注意:在这里演示顺序读写函数,我们创建了一个文本文件,且文件名为“date.txt”,里面的内容为“abcdef”。如下图:

fegtc:

字符输入函数,适用于所有输入流。对于fgetc函数的使用如下:

这里值得注意的是fgetc是读取对应流里面的内容,即打开的文件里的内容,那么我们选择打开的方式即为读文件。

fgetc函数的参数及返回类型如下图:

fgetc返回的类型为int,参数为FILE*指针,故上面代码的打印结果为‘a’。

注意:当fgetc读取完文件的中的a后,那么对应的光标会移动岛下一个字符的前面,也就是说我们下一次使用该函数读取的字符应该为‘b’。

fputc:

字符输出函数,适用于所有输出流。 对于fputc函数的使用如下:

这里也需要注意,我们要使用fputc函数写文件,那么我们的打开方式就为‘w’,即为写文件。

fputc函数的参数及返回类型如下图:

对于返回值,如果fputc写入成功,会返回写入字符串的ASCII码值;如果写入失败,则会返回EOF(通常为-1)。第一个参数为写入的字符内容,第二个是写入到哪个流,即写到哪个文件。

注意:当我们以读的方式打开文件,这个文件不管是否存在并且是否文件中有内容,以读的方法打开的话文件里的内容都会清空。

fgets:

文本行输入函数,适用于所有输入流。对于fgets函数的使用如下图:

与上述的fgetc类似,打开文件的方式都是以读的方式进行。对于fgets函数的参数及返回类型如下图:

对于fgets函数的使用,我们可以简单的理解为读取对应流(文件)中的字符串,这与fgetc不同,fgetc读取的是一个字符。

fegts的返回类型为char*的指针,这个指针指向用于存储读取到的字符串的数组,如上图代码,我们创建了一个字符数组arr,fegts函数如果读取成功,将返回数组arr的首元素地址,如果读取失败,那么会返回空指针(NULL)。

fegts函数的参数部分,首先是用于存储字符串数组的地址;接着的是要读取的字节大小(即读取的个数),最后是要读取的流(文件)。

注意:如上面的代码,我们要读取的字符数为6,但是我们实际读取到的字符数为n-1个,因为最后一位为'\0'来占位。

fputs:

文本行输出函数,适用于所有输出流。对于fputs函数的使用如下图:

与fputc函数类似,对文件的打开方式为‘w’,即写文件,并且对应的文件里的内容会清空。

对于fputs函数的参数及返回类型如下图:

对于fputs函数的返回值为int,如果写入成功,通常会返回写入字符的个数,但是c标准未明确规定具体的数值,只能保证非负。

对于fputs函数的第一个参数为要写入的字符串,与fputc函数不同,fputc函数写入的是一个字符;第二个参数则为写入的流(即文件)。

fscanf:

在演示fscanf函数前,我们要打开对应的文件中的内容如下图:

格式化输入函数,使用于所有输入流。对于fcanf函数的使用如下图:

fscanf函数与scanf函数类似,这里我们是从文件中读取格式化的数据给结构体变量s,并且打印出来的结果为:neymar 20 100.0 。对于fscanf函数的参数及返回类型如下图:

对于fscanf函数的返回类型,如果函数使用陈工,它会返回成功匹配并赋值的参数个数,但是如果读取过程中格式匹配失败,则会返回当前成功赋值的参数个数,如果是遇到文件的末尾,返回EOF(通常为-1)。

对于fscanf函数的第一个参数为要读取的流(即文件),第二个参数是格式化的字符串,包含格式说明符。

注意:fscanf函数与scanf函数类似,scanf函数是从标准输入流(stdin)中读取格式化的数据,即从键盘上读取数据,而fscanf函数则是从其他流中(包括标准输入流)读取格式化的数据。

fprintf:

格式化输出函数,适用于所有输出流。对于fprintf函数的使用如下图:

fprintf函数和printf函数其实是相似的,fprintf函数写入的对象是所有输出流,而printf函数针对的是标准输出流stdout,故这里运行的结果为文件"date.txt"文件中被写入信息:

对于fprintf函数的参数及返回类型如下图:

对于fprintf函数的返回值,如果写入成功,fprintf函数会返回实际输出的字符个数;如果写入失败,fprintf函数会返回一个负数。

对于fprintf函数的第一个参数为要写入到的流(即文件);第二个参数为格式化字符串,包含普通字符和格式说明符。

注意:这里fprintf函数和printf函数是类似的,我们只要能写出printf函数的格式,那么对于fprintf函数,只不过是把内容写入其他流中,而printf函数实际上就是写到标准输出流stdout(屏幕)上。

fread:

对于fread函数的演示前,我们这里先创建一个用二进制写入的文件,其中内容为:ne 20 100.0 ,以二进制的形式写入则如下图:

二进制输入,适用于文件输入流。对于fread函数的使用如下图:

对于fread函数,我们对文件的打开方式是以二进制的方式进行读文件,故这里打开方式则为"rb",对于上述代码,其打印结果为:ne 20 100.000000。对于fread函数的参数及返回类型如下图:

对于fread函数的返回值,如果函数使用成功,则会实际成功读取的数据项数目,如果是返回值小于count,可能是因为达到文件末尾或者读取出错。

对于fread函数的第一个参数指向接收数据的内存缓冲区的指针;第二个参数为每个数据项的字节大小;第三个参数为期望写入的数据项数量;第四个参数为指向文件流的指针。

注意:fread函数的读取方式是以二进制的方式进行读取,故我们打开方式应为"rb",并且我们要确保ptr所指向的内存空间足够容纳size*count字节的数据,避免溢出。

fwrite:

二进制输出,适用于文件输出流。对于fwrite函数的使用如下图:

对于使用fwrite函数,我们打开文件的方式为用二进制的方式写文件,即"wr"。对于fwrite函数的参数及返回类型如下图:

对于fwrite函数的返回值为实际成功写入的数据项的数量。

对于fwrite函数的第一个参数为要写入数据的指针;第二个参数为每个数据项的字节大小;第三个参数为期望写入的数据项数量;第四个参数为指向文件流的指针。

注意:fwrite函数写入的方式是为二进制的方式,如上面的代码,我们将arr数组中的1,2,3,4,5写入到文件中,它是以二进制的形式写入的,当我们打开文件进行查看时我们往往时看不懂的,就比如下图:

补充说明:上面说的适用于所有输入流一般指适用于标准输入流和其他输入流(如文件输入流);所有输出流一啊不能指适用于标准输出流和其他输出流(如文件输出流)。

2.对比一组函数

接下来我们来看看这些函数分别由什么区别,函数如下图:

在开始之前,这里简单介绍一下sscanf函数和sprintf函数。

sscanf:

从字符串中读取格式化数据,它与scanf类似,但是输入源时内存中的字符串而非控制台或者文件,常用于解析字符串中的结构化数据(如图区数字,分割文本等)。

对于sscnaf函数的参数及返回类型如下图:

对于sscanf函数的返回值,当我们使用成功时它会返回成功解析并赋值的参数数量(非负值);当使用失败时返回EOF(通常为-1)。

对于sscanf函数的第一个参数s为要解析的字符串;第二个参数format为格式化字符串,定义如何解析数据,如(%d %f %s 等)。

对于sscanf函数的使用如下图:

在这里使用sscanf函数,我们时从字符数组中读取格式化的内容,并且我们打印出来的结果为:neymarjr 20 100.000000 。

sprintf:

将格式化的数据写入字符数组(字符串),它类似于printf,但是输出的目标不是控制台或者文件,而是用户指定的内存缓冲区。该函数常用于动态生成字符串,拼接数据或转换数据格式,简而言之就是将格式化的数据转换成一个字符串。

对于sprintf函数的参数及返回类型如下图:

对于sprintf函数的返回值,当使用成功时返回写入的字符数(不包括结尾的空字符\0);当使用失败时会返回负值。

对于sprintf函数的第一个参数str,str指向字符串数组,用于储存生成的字符串(需确保足够大以防溢出);第二个参数format指的是格式化的字符串,定义输出格式(如 %d %f %s 等)。

对于sprintf函数的使用如下图:

在这里使用sprintf函数,把格式化的数据:neymarjr 20 100.000000 格式化的存入str数组中,当我们打印都屏幕的结果为:neymarjr 20 100.000000 。

介绍完了sscanf函数和sprintf函数,接下来我们来比较一下这些函数的区别所在。

scanf:针对的是标准输入(stdin)的格式话输入函数

printf:针对标准输出(stdout)的格式化输出函数

fscanf:针对所有输入流(可以是文件流,也可以是stdin)的格式化输入函数

fprintf:针对所有输出流(可以是文件流,也可以是stdout)的格式化输出函数

sscanf:从字符串中提取格式化的数据

sprintf:将格式化的数据转化为字符串

六 . 文件的随机读写

对于文件的随机读写,我们也要去了解一下文件随机读写相关的函数。

对于接下来要介绍的函数,实际上都是在改变文件流中光标的位置!

fseek:

fssek函数用于在文件流中定位读写位置。

对于fseek函数的参数及返回类型如下图:

对于fseek函数的返回类型,使用成功时会返回0,失败时则会返回-1,并且设置errno错误码

对于fseek函数的第一个参数stream指向需要操作的文件(已打开的文件流);第二个参数offset为偏移量,表示从起始位置移动的字节数;第三个参数origin为偏移的起始基准位置。

注意:对于fseek函数的第三个参数origin,常用的取值包括下面三个:

—SEEK_STT:从文件开头开始计算偏移

—SEEK_CUR:从当前读写位置开始计算偏移

—SEEK_END:从文件末尾开始计算偏移

对于fseek函数的使用如下图:

这里我们以写的方式打开文件流,然后利用上述讲到的fputs函数写入字符串“abcdefghi”,这时候我们想让e变成x,那么我们就需要把光标指向到e之前,而此时光标在i之后,故我们使用fseek函数去改变光标的位置。

这里我们使用fseek函数,让光标从i后面移动到距离文件开始位置偏移量为4的位置,即e前d后的位置,这样我们就能把e变成x了。

当然,这里还有;另外一种写法:

我们让光标在文件末尾的位置,即在i后的位置,向前偏移5个位置,所有这里的偏移量为-5,这样也可以把e修改为x。

ftell:

ftell函数的作用是返回文件指针相对于起始位置的偏移量,即此时的光标距离起始位置的偏移量。

对于ftell函数的参数及返回类型如下图:

对于ftell函数的返回值,使用成功时返回当前位置相对于文件开头的字节偏移量(长整型);失败时返回-1L,并且设置errno错误码。

对于ftell函数的参数stream指的是需要操作的文件。

对于ftell函数的使用如下图:

对于ftell函数的使用,在上述代码中,此时的光标位置在d后e前的位置,举例文件起始位置的偏移量为4,故len的值应该为4。

rewind:

rewind函数的作用是让文件指针的位置回到文件的起始位置。

对于rewind函数的参数及返回类型如下图:

rewind函数无返回类型。

对于rewind函数的参数stream指的是需要操作的文件指针。

对于rewind函数的使用如下图:

我们这里利用fputs函数写入字符串"abcdefghi",此时光标在i之后,我们可以使用rewind函数使得光标回到起始位置,即回到a之前的位置。

注意:对于rewind函数的功能,其实我们使用fseek函数也可以实现相同的效果,比如下图:

七 . 文件读取结束的判定

我们在读取文件的过程中,对于文件读取的结束我们会有两种情况。第一种为读取到了文件末尾了;第二种为读取文件过程中出现错误。

那么为了能够判断文件读取结束的原因,我们就需要去了解虾米那这两个函数。

feof:

feof函数的作用是当文件读取结束时,判断读取结束的原因是否是:遇到文件末尾结束

对于feof函数的参数及返回类型如下图:

对于feof函数的返回值,如果文件指针已经到达文件末尾,返回非零值(通常为1);如果没有到达文件末尾,则返回0。

对于feof函数的参数stream,指向需要检测的文件流。

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

对于文本文件读取是否结束,我们需要判断返回值是否为EOF或者是NULL。比如fgetc判断手否为EOF;fgets判断是否为NULL。

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

ferror:

ferror函数的作用是当文件读取结束时,判断读取结束的原因是否是:读取过程发生错误

对于ferror函数的参数及返回类型如下图:

对于ferror函数的返回值,如果文件流在最经一次操作中发生错误,则会返回非0值;如果没有发生错误,则返回0。

对于ferror函数的参数stream指的是需要判断的文件流。

注意:ferror函数的feof函数都是用于判断文件读取结束的原因,ferror函数判断是否是读取过程中出错而导致的;而feof函数则是判断是否是读取到文件末尾所导致的。

八 . 文件缓冲区

ANSI C 标准采用"缓冲文件系统"来处理数据文件的,所谓缓冲文件系统是指系统自动地子啊内存中为程序中每一个正在使用的文件开辟"文件缓冲区"。

从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后次啊一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区。

缓冲区的大小根据C编译系统决定的。

下图我们可以更直观的去理解缓冲区:

对于缓冲区是否存在,我们可以用下列代码进行观察:

补充:对于缓冲区的刷新,不仅仅是当程序终止或者是缓冲区已满,我们也可以主动条用刷新函数fflush函数区刷新缓冲区。

对于fflush函数的参数及返回类型如下图:

对于fflush函数的返回值,当刷新成功时返回0;如果发生错误则会返回EOF。

对于stream,指的是需要刷新缓冲区的文件流。

结论:因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者再文件操作结束的时候关闭文件。

总结:以上是我个人对文件操作的理解,如果有不对的地方,劳烦各位大佬帮忙指点!

内容概要:本文系统研究了Picard迭代法在非线性常微分方程参数估计中的应用,深入阐述了该方法的数学原理及其在参数辨识中的收敛性与稳定性优势。通过构建最小化误差的目标函数,并结合数值积分技术,采用迭代方式逐步逼近系统的真实参数值,有效解决了非线性动态系统中因缺乏解析解而难以进行精确建模的问题。文中提供了完整的Matlab代码实现,涵盖模型定义、迭代求解、参数更新与结果可视化等关键环节,增强了方法的可操作性与工程实用性。研究通过典型非线性系统案例验证了算法的有效性,展示了其在科学计算与工程建模中的良好适应性与推广潜力。; 适合人群:具备常微分方程理论、数值分析基础及Matlab编程能力,从事系统建模、参数辨识、动力学仿真等相关方向的研究生、科研人员和工程技术开发者。; 使用场景及目标:①解决实际工程中非线性微分方程模型的未知参数估计问题;②深入理解Picard迭代法在科学计算中的实现机制与数值特性;③为学术论文复现、科研项目开发或课程设计提供可运行、易调试的技术方案与代码参考。; 阅读建议:建议读者结合文中的数学推导与Matlab代码逐行分析,重点关注迭代流程、目标函数构造与数值积分的耦合实现,通过修改模型结构或噪声条件进行扩展实验,以深化对算法鲁棒性与适用边界的理解。配套资源可通过指定公众号和网盘链接获取,推荐同步学习以加速科研进程。
内容概要:本文详细介绍了一种基于多尺度集成极限学习机(Extreme Learning Machine, ELM)的回归方法,并提供了完整的Matlab代码实现。该方法通过构建多尺度特征表示与集成学习机制,有效提升了ELM在处理非线性、高维复杂数据时的预测精度与模型鲁棒性,特别适用于时间序列回归任务。文档不仅阐述了算法的核心原理与技术流程,还系统展示了其在风电功率预测等工程场景中的应用潜力。同时,文中附带了丰富的科研仿真案例集合,涵盖智能优化算法、深度学习、信号处理、电力系统调度等多个前沿方向,体现了多学科交叉融合的技术优势与实践价值。; 适合人群:具备一定Matlab编程能力,从事科学研究或工程应用的研究生、科研人员及工程技术开发者,尤其适合专注于机器学习、智能算法优化、新能源预测与电力系统建模等相关领域的专业人员。; 使用场景及目标:①用于风电、光伏、负荷等时间序列数据的高精度回归预测任务;②为科研工作者提供可复现的多尺度集成ELM模型代码框架,支持快速算法验证与二次开发;③满足实际工程项目中对高效建模、实时预测与智能决策的技术需求。; 阅读建议:建议读者结合所提供的Matlab代码进行动手实践,深入理解多尺度特征构造与集成策略的设计思想,同时可参考文档中其他相关算法案例进行横向比较与综合应用,以提升整体科研创新能力。
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值