Fork me on GitHub

C语言内存分配文件读写的相关基本函数解析

fopen()函数解析

fopen()函数的功能是打开一个文件,其调用的一般形式为:

文件指针名=fopen(文件名,使用文件方式)

“文件指针名”必须是被声明为FILE 类型的指针变量;

“文件名”是被打开文件的文件名,类型是C风格字符串;

“使用文件方式”是指文件的类型和操作要求。

例如:

FILE *fp;

fp=fopen("TEXT.txt","r");

其意义是在当前目录下打开文件TEXT.txt,只允许进行“读”操作,并使fp指向该文件。

又如:

FILE *fp

fp=fopen("c:\\HYH","rb");

其意义是打开C驱动器磁盘的根目录下的文件HYH,按二进制方式进行读操作。两个反斜线“\ ”中的第一个表示转义字符,第二个表示根目录。

判断文件是否打开成功

打开文件出错时,fopen() 将返回一个空指针,也就是 NULL,我们可以利用这一点来判断文件是否打开成功,请看下面的代码:

FILE *fp;
if( (fp=fopen("D:\\TEXT.txt","rb")) == NULL ){
    printf("Fail to open file!\n");
    exit(0);  //退出程序(结束程序)
}

我们通过判断 fopen() 的返回值是否和 NULL 相等来判断是否打开失败:如果 fopen() 的返回值为 NULL,那么 fp 的值也为 NULL,此时 if 的判断条件成立,表示文件打开失败。

以上代码是文件操作的规范写法,读者在打开文件时一定要判断文件是否打开成功,因为一旦打开失败,后续操作就都没法进行了,往往以“结束程序”告终。

fopen()函数的打开方式

不同的操作需要不同的文件权限。例如,只想读取文件中的数据的话,“只读”权限就够了;既想读取又想写入数据的话,“读写”权限就是必须的了。

另外,文件也有不同的类型,按照数据的存储方式可以分为二进制文件和文本文件,它们的操作细节是不同的。

在调用 fopen() 函数时,这些信息都必须提供,称为“文件打开方式”。最基本的文件打开方式有以下几种:

控制读写权限的字符串(必须指明)
**打开方式 ** 说明
"r" 以“只读”方式打开文件。只允许读取,不允许写入。文件必须存在,否则打开失败。
"w" 以“写入”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。
"a" 以“追加”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。
"r+" 以“读写”方式打开文件。既可以读取也可以写入,也就是随意更新文件。文件必须存在,否则打开失败。
"w+" 以“写入/更新”方式打开文件,相当于wr+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。
"a+" 以“追加/更新”方式打开文件,相当于a和r+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)
控制读写方式的字符串(可以不写)
打开方式 说明
"t" 文本文件。如果不写,默认为"t"
"b" 二进制文件。

调用 fopen() 函数时必须指明读写权限,但是可以不指明读写方式(此时默认为"t")。

读写权限和读写方式可以组合使用,但是必须将读写方式放在读写权限的中间或者尾部(换句话说,不能将读写方式放在读写权限的开头)。例如:

  • 将读写方式放在读写权限的末尾:"rb"、"wt"、"ab"、"r+b"、"w+t"、"a+t"
  • 将读写方式放在读写权限的中间:"rb+"、"wt+"、"ab+"

整体来说,文件打开方式由 r、w、a、t、b、+ 六个字符拼成,各字符的含义是:

  • r(read):读
  • w(write):写
  • a(append):追加
  • t(text):文本文件
  • b(binary):二进制文件
  • +:读和写

fclose()函数解析

fclose是一个函数名,功能是关闭一个流。

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

函数原型:int fclose( FILE* fp )

返回值:如果流成功关闭,fclose 返回 0,否则返回EOF(-1)。(如果流为NULL,而且程序可以继续执行,fclose设定error number给EINVAL,并返回EOF。)

下面的实例演示了 fclose() 函数的用法

#include <stdio.h>
int main(){
   FILE *fp;//声明一个为FILE类型的文件指针名
   fp = fopen("file.txt", "w");//以写入方式打开一个名为file.txt的文件
   fprintf(fp, "%s", "这里是 runoob.com");//将该字符串写入文本
   fclose(fp);//使用fclose()函数关闭文件
   return(0);
}

*我们知道fclose(FILE )可以关闭文件流,但是我们偶尔也会发现似乎不加也没有什么问题。那我们为什么还要添加呢?

一、清空相关的缓冲区

以写文件为例,程序会先将文本信息存入缓冲区中,然后根据缓冲区刷新指令或者等缓冲区满了以后才向文件写入,那么我们如果不使用fclose()关闭流,就有可能有部分文本信息在缓冲区中没有写入到文本文件中,从而导致文本信息缺失.但是这东西看运气.

二、释放内存

我们知道,如果我们不手动关闭这个流,系统会维持打开的状态,维持当然需要消耗一定的内存资源,本着苍蝇再小也是块肉的原则当然是要把它关掉了,其实如果有很多流在使用后没有关闭,这部分内存资源也是十分可观的.

fseek()函数解析

*函数原型:int fseek(FILE stream, long offset, int fromwhere)

函数设置文件指针stream的位置

如果执行成功,stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。如果执行失败(比如offset取值大于等于210241024*1024,即long的正数范围2G),则不改变stream指向的位置,函数返回一个非0值。

fseek函数和lseek函数类似,但lseek返回的是一个off_t数值,而fseek返回的是一个整型。

函数功能:定位流(数据流/文件)上的文件内部位置指针

注意:文件指针指向文件/流。位置指针指向文件内部的字节位置,随着文件的读取会移动,文件指针如果不重新赋值将不会改变或指向别的文件。

描述:函数设置文件指针stream的位置。如果执行成功,stream将指向以fromwhere(偏移起始位置:文件头0(SEEK_SET),当前位置1(SEEK_CUR),文件尾2(SEEK_END))为基准,偏移offset(指针偏移量)个字节的位置。如果执行失败(比如offset超过文件自身大小),则不改变stream指向的位置。fseek函数和lseek函数类似,但lseek返回的是一个off_t数值,而fseek返回的是一个整型。

下面的实例演示了 fclose() 函数的用法:

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

第一个参数stream为文件指针

第二个参数offset为偏移量,正数表示正向偏移,负数表示负向偏移

第三个参数origin设定从文件的哪里开始偏移,可能取值为:SEEK_CUR、 SEEK_END 或 SEEK_SET

SEEK_SET: 文件开头

SEEK_CUR: 当前位置

SEEK_END: 文件结尾

其中SEEK_SET,SEEK_CUR和SEEK_END依次为0,1和2.

简言之:

fseek(fp,100L,0);把stream指针移动到离文件开头100字节处;

fseek(fp,100L,1);把stream指针移动到离文件当前位置100字节处;

fseek(fp,-100L,2);把stream指针退回到离文件结尾100字节处。

ftell()函数解析

ftell() 函数的作用是 获取文件的 当前指针位置 相对于 文件首地址 的 偏移字节数

ftell函数原型:longftell(FILE* stream);

ftell 使用场景 : 随机方式存取文件时 , 使用 fseek 函数来回移动指针 , 不容易确定当前指针位置 , 通过调用 ftell 函数确定指针位置

使用 ftell 函数获取当前指针位置

代码示例 :

  • 第一次打印指针位置 : 刚打开文件 , 还未读取数据 , 指针指向 0 位置 , 索引 0 ;
  • 第二次打印指针位置 : 跳过了 4 字节数据 , 偏移了 4 字节 , 指针指向第 5 字节 , 索引 4 ;
  • 第三次打印指针位置 : 跳过了 4 字节数据 , 又向后读取 2 字节 , 偏移了 6 字节 , 指向第七字节 , 索引 6 ;
#include <stdio.h>

int main()
{
    // 以写文本的方式向文件中写出数据
    FILE *p = fopen("D:/File/number.dat", "w");
   
    // 写出 10 个字符
    char array[10] = {1,2,3,4,5,6,7,8,9,10};
    
    // 将 10 个字符写出到文件中
    fwrite(array, 1, sizeof(array), p);
   
    // 关闭文件
    fclose(p);
    
    // 跳过 4 字节读取文件 2 字节
    FILE *p2 = fopen("D:/File/number.dat", "r");
   
    // 使用 ftell 函数获取当前指针位置
    int location = ftell(p2);
   
    //第一次打印指针位置:刚打开文件,还未读取数据,指针指向0位置,索引0;
    printf("location = %d\n", location);
   
    // 读取数据缓冲区
    char array2[2];

    // 跳过 4 字节 , 此时当前位置是 0
    fseek(p2, 4, SEEK_CUR);

    // 使用 ftell 函数获取当前指针位置
    location = ftell(p2);
    
    //第二次打印指针位置:跳过了4字节数据,偏移了4字节,指针指向第5字节,索引4;
    printf("location = %d\n", location);
   
    // 读取 2 字节
    fread(array2, 1, sizeof(array2), p2);
    
    // 打印前 2 字节数据
    printf("%d , %d\n", array2[0], array2[1]);
   
    // 使用 ftell 函数获取当前指针位置
    location = ftell(p2);
    //第三次打印指针位置:跳过了4字节数据,又向后读取2字节,偏移了6字节,指向第七字节,索引6;
    printf("location = %d\n", location);
    return 0;
}


fscanf()函数解析

fscanf 函数原型为 int fscanf(FILE * stream, const char * format, [argument...])

其功能为根据数据格式(format),从输入流(stream)中读入数据,存储到argument中,遇到空格和换行时结束。

fscanf位于C标准库头文件<stdio.h>中。

功 能:从一个流中执行格式化输入,fscanf遇到空格和换行时结束,注意空格时也结束。这与fgets有区别,fgets遇到空格不结束。

返回值:整型,成功返回读入的参数的个数,失败返回EOF(-1)。

fscanf 函数的原型是:

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

fscanf 函数与 scanf 函数用法类似,只不过前者用于读取文件流的数据而已。至于 fscanf 的基础用法我就不赘述了,网上的文章很多。简单提及一下要点:

  1. format str:如%d, %f, %c, %s等,分别表示读入一个整数,浮点数,字符,字符串。还可以加上控制如%ld,表示读入一个长整型数,%20s表示最多读入20个字符。
  2. 返回值:在没有出错的情况下,fscanf 返回正确匹配和赋值的域的个数;如果出错,则返回EOF。

fscanf*的难点在于以下几点:

  1. 对空白符的处理(空格、制表符、换行符);在空白符这个意义上来讲,fscanf 对空格、制表符、换行符是一视同仁的,不加区分的;%s会跳过前面的空白符,但是不会跳过后面的空白符;%c不会跳过空白符。
  2. *的用法; * 表示读取一个域,但是不赋值给变量。
  3. [] 以及 [^] 的用法;[]表示只读取中括号内的字符,[]表示不读取中括号内的字符,值得注意的是%[]s将不会跳过前面的空白符。
  4. EOF 的处理;如果还没有任何一个域匹配成功或者任何一个匹配失败发生之前,就达到了文件流末尾,就算出错;或者读取文件流出错。这两种情况下,fscanf 返回EOF。

fprintf()函数解析

函数声明

int fprintf (FILE* stream, const char*format, [argument])

参数

  • stream-- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
  • format-- 这是 C 字符串,包含了要被写入到流 stream 中的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。format 标签属性是%[flags][width][.precision][length]specifier
  • [argument]:附加参数列表

功能:fprintf()函数根据指定的格式(format),向输出流(stream)写入数据(argument)。

函数说明:fprintf( )会根据参数format 字符串来转换并格式化数据,然后将结果输出到参数stream 指定的文件中,直到出现字符串结束('\0')为止。

posted @ 2023-01-08 19:53  汁汁小汉堡  阅读(595)  评论(0)    收藏  举报