深入Linux文件编程核心,编织数据的经纬,构建高效系统的坚实基石!#Linux系统编程中的文件编程(下)

前言

  本篇博文详尽而透彻地探讨了Linux系统编程中的文件操作进阶知识(下),从标准C库对文件操作的引入开始,深入讲解了如何打开与创建文件、文件的读写操作、以及如何在文件中移动光标。此外,还介绍了如何将结构体数据写入文件,并详细说明了标准C库中fputc、fgetc和feof等函数的用法。我们诚挚建议您点赞并收藏此篇博文,以便日后反复研读,逐步精进您的编程技能。

预备知识

  一、C变量
  二、基本输入输出
  三、流程控制
  四、函数
  五、指针
  六、字符串
  七、Linux系统基本操作命令如mkdir,ls -l等。

  如果以上知识不清楚,请自行学习后再来浏览。如果我有没例出的,请在评论区写一下。谢谢啦!

一、 标准C库对文件操作引入

1.1 来源

  从来源的角度看,两者能很好的区分开,这也是两者最显而易见的区别:
  open是UNIX系统调用函数(包括LINUX等),返回的是文件描述符(File Descriptor),它是文件在文件描述符表里的索引。
  fopen是ANSIC标准中的C语言库函数,在不同的系统中应该调用不同的内核api。返回的是一个指向文件结构的指针。
  PS:从来源来看,两者是有千丝万缕的联系的,毕竟C语言的库函数还是需要调用系统API实现的。

1.2 移植性

  这一点从上面的来源就可以推断出来,fopen是C标准函数,因此拥有良好的移植性;而open是UNIX系统调用,移植性有限。如windows下相似的功能使用API函数CreateFile

1.3 适用范围

  open返回文件描述符,而文件描述符是UNIX系统下的一个重要概念,UNIX下的一切设备都是以文件的形式操作。如网络套接字、硬件设备等。当然包括操作普通正规文件(Regular File)。
  fopen是用来操纵普通正规文件(Regular File)的。

1.4 文件IO层次

  如果从文件IO的角度来看,前者属于低级IO函数,后者属于高级IO函数。低级和高级的简单区分标准是:谁离系统内核更近。低级文件IO运行在内核态,高级文件IO运行在用户态。

1.5 缓冲

  缓冲文件系统
  缓冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用;当执行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”,装满后再从内存“缓冲区”依此读出需要的数据。执行写文件的操作时,先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件。由此可以看出,内存“缓冲区”的大小,影响着实际操作外存的次数,内存“缓冲区”越大,则操作外存的次数就少,执行速度就快、效率高。一般来说,文件“缓冲区”的大小随机器 而定。fopen, fclose, fread, fwrite, fgetc, fgets, fputc, fputs, freopen, fseek, ftell, rewind等。
  非缓冲文件系统
  缓冲文件系统是借助文件结构体指针来对文件进行管理,通过文件指针来对文件进行访问,既可以读写字符、字符串、格式化数据,也可以读写二进制数据。非缓冲文件系统依赖于操作系统,通过操作系统的功能对文件进行读写,是系统级的输入输出,它不设文件结构体指针,只能读写二进制文件,但效率高、速度快,由于ANSI标准不再包括非缓冲文件系统,因此建议大家最好不要选择它。open, close, read, write, getc, getchar, putc, putchar等。
  一句话总结一下,就是open无缓冲,fopen有缓冲。前者与read, write等配合使用, 后者与fread,fwrite等配合使用。

  使用fopen函数,由于在用户态下就有了缓冲,因此进行文件读写操作的时候就减少了用户态和内核态的切换(切换到内核态调用还是需要调用系统调用API:read,write);而使用open函数,在文件读写时则每次都需要进行内核态和用户态的切换;表现为,如果顺序访问文件,fopen系列的函数要比直接调用open系列的函数快;如果随机访问文件则相反。
(以上文章来源于NickyYe所著原文链接如下)
点击这里

二、 标准C库打开创建文件读写文件光标移动

2.1 fopen函数介绍

SYNOPSIS
       #include <stdio.h>

       FILE *fopen(const char *path, const char *mode);
	   path:文件路径 ,mode:打开的权限,记住是字符串 
       FILE *fdopen(int fd, const char *mode);

       FILE *freopen(const char *path, const char *mode, FILE *stream);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       fdopen(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

mode:
	   r      Open text file for reading.  The stream is positioned at  the
              beginning of the file.

       r+     Open  for  reading  and writing.  The stream is positioned at
              the beginning of the file.

       w      Truncate file to zero length or create text file for writing.
              The stream is positioned at the beginning of the file.

       w+     Open for reading and writing.  The file is created if it does
              not exist, otherwise it is truncated.  The  stream  is  posi‐
              tioned at the beginning of the file.

       a      Open  for  appending  (writing  at end of file).  The file is
              created if it does not exist.  The stream  is  positioned  at
              the end of the file.

       a+     Open for reading and appending (writing at end of file).  The
              file is created if it does not exist.  The initial file posi‐
              tion  for reading is at the beginning of the file, but output
              is always appended to the end of the file.
译:
		r   只读方式打开一个文本文件                                                  
        w   只写方式打开一个文本文件                                               
        a   追加方式打开一个文本文件                           
        r+  可读可写方式打开一个文本文件                       
        w+  可读可写方式创建一个文本文件                       
        a+  可读可写追加方式打开一个文本文件   
 
RETURN VALUE
       Upon  successful completion fopen(), fdopen() and freopen() return a
       FILE pointer.  Otherwise, NULL is returned and errno is set to indi‐
       cate the error.     
译:
	   返回值
	   成功完成fopen()后,fdopen()和freopen()返回一个FILE指针。否则,将返回NULL,并设置errno来指示错误。           

2.2 fwrite函数介绍

size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
ptr:数据缓冲区
size:单个字符大小
nmemb:写入字符的个数
stream: 文件指针

2.3 fseek函数介绍

int fseek(FILE *stream, long offset, int whence);
stream: 文件指针
offset:偏移量
whence:固定位置
用法和lseek函数一致

2.4 fread函数介绍

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
ptr:数据缓冲区
size:单个字符大小
nmemb:写入字符的个数
stream: 文件指针

2.5 fclose函数介绍

int fclose(FILE *fp);
fp:文件指针

2.6 fwrite和fread返回值补充

2.6.1 fwrite和fread返回值man手册介绍
RETURN VALUE
       fread() and fwrite() return the number of items successfully read or
       written (i.e., not the number of characters).  If an  error  occurs,
       or  the  end-of-file  is  reached,  the return value is a short item
       count (or zero).

       fread() does not distinguish  between  end-of-file  and  error,  and
       callers must use feof(3) and ferror(3) to determine which occurred.

  译:
  返回值
  fread()和fwrite()返回成功读取或写入的项目数(即,不是字符数)。如果发生错误或到达文件末尾,则返回值为短项目计数(或零)。
  fread()不区分文件结尾和错误,调用者必须使用feof(3)和ferror(3)来确定发生了什么。

2.6.2 程序验证fwrite和fread函数返回值

  程序代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc,char **argv)
{
        FILE *fp = NULL;
        char *buf= "fwrite successful write to file";
        char *readBuf = NULL;
        size_t n_write = 0;           size_t 是无符号整型
        size_t n_read  = 0;


        //FILE *fopen(const char *path, const char *mode);
        fp = fopen("./text.txt","w+");
        //size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
        n_write = fwrite(buf,sizeof(char),strlen(buf),fp);
        //int fseek(FILE *stream, long offset, int whence);
        fseek(fp,0,SEEK_SET);
        readBuf = (char *)malloc(sizeof(char) * strlen(buf));
        //size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
        n_read = fread(readBuf,sizeof(char),strlen(buf),fp);
        printf("readBuf = %s\n",readBuf);
        printf("n_write = %zu,n_read = %zu.\n",n_write,n_read);输出size_t类型用zu格式控制符,z代表size_t,u表示无符号
        printf("strlen(buf) = %zu\n",strlen(buf));  strlen函数返回的也是size_t类型
        fclose(fp);

        return 0;
}

  程序运行结果

CLC@Embed_Learn:~/Linux_System_Programming/Linux_file_programming/11_Standard_C_Library_open_creat_write_read_seek_to_file$ gcc File_programming2.c 
CLC@Embed_Learn:~/Linux_System_Programming/Linux_file_programming/11_Standard_C_Library_open_creat_write_read_seek_to_file$ ./a.out 
readBuf = fwrite successful write to file
n_write = 31,n_read = 31.
strlen(buf) = 31

  从这里可以得到初步结论,读取和写入函数的返回值和改函数的第三个参数有关
  那如果fwrite的第三个参数和fread的第三个参数不一致呢,结果又是怎样?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc,char **argv)
{
        FILE *fp = NULL;
        char *buf= "fwrite successful write to file";
        char *readBuf = NULL;
        size_t n_write = 0;
        size_t n_read  = 0;
        size_t x_write = 100;100size_t x_read  = 31;31//FILE *fopen(const char *path, const char *mode);
        fp = fopen("./text.txt","w+");
        //size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
        n_write = fwrite(buf,sizeof(char),x_write,fp);
        //int fseek(FILE *stream, long offset, int whence);
        fseek(fp,0,SEEK_SET);
        readBuf = (char *)malloc(sizeof(char) * strlen(buf));
        //size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
        n_read = fread(readBuf,sizeof(char),x_read,fp);
        printf("readBuf = %s\n",readBuf);
        printf("n_write = %zu,n_read = %zu.\n",n_write,n_read);
        printf("strlen(buf) = %zu\n",strlen(buf));
        fclose(fp);

        return 0;
}

  程序运行结果

CLC@Embed_Learn:~/Linux_System_Programming/Linux_file_programming/11_Standard_C_Library_open_creat_write_read_seek_to_file$ gcc File_programming3.c 
CLC@Embed_Learn:~/Linux_System_Programming/Linux_file_programming/11_Standard_C_Library_open_creat_write_read_seek_to_file$ ./a.out 
readBuf = fwrite successful write to file
n_write = 100,n_read = 31.
strlen(buf) = 31

  当x_read的值小于或等于buf数组的实际长度时,代码执行不会出现段错误。然而,如果x_read被错误地设置为大于buf数组的长度,程序在执行时可能会因为尝试访问非法内存区域而引发段错误。在使用fwrite和fread时,确保它们的第三个参数(即请求写入或读取的最大字节数)小于或等于目标缓冲区或源数据的实际长度,以避免此类问题,并且它们的返回值是等于第三个参数的值。

三、 标准C写结构体到文件

3.1 标准C写结构体到文件思路

  标准C写结构体到文件和Linux文件编程是一样的,只不过使用的API不一样而已。

3.2 标准C写结构体到文件程序代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct Text
{
        int a;
        char c;
};

int main(int argc,char **argv)
{
        FILE *fp = NULL;
        struct Text data = {100,'c'};
        struct Text data2;

        //FILE *fopen(const char *path, const char *mode);
        fp = fopen("./text.txt","w+");
        //size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
        fwrite(&data,sizeof(struct Text),1,fp);
        //int fseek(FILE *stream, long offset, int whence);
        fseek(fp,0,SEEK_SET);
        //size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
        fread(&data2,sizeof(struct Text),1,fp);
        printf("data2.a = %d data2.c = %c\n",data2.a,data2.c);
        fclose(fp);

        return 0;
}

3.3 标准C写结构体到文件程序运行结果

CLC@Embed_Learn:~/Linux_System_Programming/Linux_file_programming/12_Standard_C_Library_write_structure_to_file$ gcc File_programming.c 
CLC@Embed_Learn:~/Linux_System_Programming/Linux_file_programming/12_Standard_C_Library_write_structure_to_file$ ./a.out 
data2.a = 100 data2.c = c

四、 标准C库fputc,fgetc,feof

4.1 fputc,fgetc,feof介绍

4.1.1 fputc介绍

   函数原型

  fputc函数的原型定义在stdio.h头文件中,其原型如下:

int fputc(int char, FILE *stream);

  **注意:**虽然函数原型中的第一个参数是int类型,但实际上只使用它的低八位来表示字符(即ASCII码值)。FILE *stream参数是指向FILE对象的指针,该对象标识了要写入字符的文件。

  函数功能

  fputc函数的功能是将单个字符写入到由stream参数指定的文件的当前写指针位置。写入成功后,文件的内部写指针会自动向后移动一个字符的位置。

  返回值

  如果函数成功执行,即字符被成功写入文件,则返回写入的字符的ASCII码值。
  如果发生错误(如文件指针无效、磁盘空间不足等),则返回EOF(在stdio.h中定义为宏,通常为-1)。

4.1.2 fgetc介绍

  函数原型

  fgetc函数的原型定义在stdio.h头文件中,其原型如下:

int fgetc(FILE *stream);

  其中,FILE *stream是指向FILE对象的指针,该对象标识了要从中读取字符的文件。

  函数功能

  fgetc函数的功能是从stream参数指定的文件的当前读指针位置读取一个字符。读取成功后,文件的内部读指针会自动向后移动一个字符的位置。

  返回值

  如果函数成功执行,即成功读取到字符,则返回读取到的字符的ASCII码值。
  如果到达文件末尾(EOF)或发生读取错误,则返回EOF(在stdio.h中定义为宏,通常为-1)。需要注意的是,EOF是一个有效的整型值,用于表示文件结束或读取错误。

4.1.3 feof介绍

  函数原型

  feof函数的原型定义在stdio.h头文件中,其原型为:

int feof(FILE *stream);

  其中,FILE *stream是指向一个已打开的文件的文件指针。

  函数功能

  feof函数的功能是检测文件流stream是否已经到达了文件结尾。如果文件流已经到达文件结尾,则函数返回非零值(通常为1,但具体值可能依系统而异);如果文件流尚未到达文件结尾,则返回0。

  工作原理

  feof函数的工作原理并不是直接读取文件内容来判断是否到达文件结尾,而是检查与文件流相关联的结束标志。当文件读取操作(如fgetc、fgets、fread等)到达文件末尾时,这些函数会设置文件流的结束标志。随后,调用feof函数就可以根据这个结束标志来判断文件是否已经读取完毕。
  需要注意的是,feof函数在读取操作之前调用是没有意义的,因为它不会改变或检查文件流的状态。正确的做法是在读取操作之后调用feof函数来检查是否到达文件结尾。

4.2 fputc程序

4.2.1 fputc写入一个字符

  程序代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


int main(int argc,char **argv)
{
        FILE *fp = NULL;

        fp = fopen("./text.txt","w+"); 打开或创建文件
        fputc('c',fp);                 向文件写入字符c
        fclose(fp);					   关闭文件

        return 0;
}

  程序运行结果

在text.txt文件中存在写入的字符c
c
4.2.2 fputc写入字符串

  程序代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


int main(int argc,char **argv)
{
        FILE *fp = NULL;
        char *str = "fputc successful to file!";     定义写入的字符串
        int i; 
        int len = strlen(str);                       计算写入字符串的长度

        fp = fopen("./text.txt","w+");               打开或创建文件
        for(i=0; i<len; i++)                         向文件中一个一个写入字符
        {
                fputc(*str,fp);                   
                str++;
        }
        fclose(fp);                                  关闭文件

        return 0;
}

  程序运行结果

在text.txt文件中存在写入的字符串fputc successful to file!
fputc successful to file!

4.3 fputc、fgetc、feol综合运用

4.3.1 fputc、fgetc、feol综合运用程序代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


int main(int argc,char **argv)
{
        FILE *fp = NULL;
        char *str = "fputc successful to file!";	定义写入的字符串
        char c;
        int i;
        int len = strlen(str);                      计算写入字符串的长度

        fp = fopen("./text.txt","w+");              打开或创建文件
        for(i=0; i<len; i++)                        向文件中一个一个写入字符
        {
                fputc(*str,fp);
                str++;
        }
        fseek(fp,0,SEEK_SET);                       移动光标到文件前端
        while(!feof(fp))                            输出文件中的字符串
        {
                c = fgetc(fp);                      获取一个字符
                printf("%c",c);                     输出这个字符
        }
        putchar('\n');
        fclose(fp);                                 关闭文件

        return 0;
}
4.3.2 fputc、fgetc、feol综合运用程序运行结果
CLC@Embed_Learn:~/Linux_System_Programming/Linux_file_programming/13_Standard_C_Library_fgetc_fputc_feof$ gcc File_programming3.c 
CLC@Embed_Learn:~/Linux_System_Programming/Linux_file_programming/13_Standard_C_Library_fgetc_fputc_feof$ rm text.txt 
CLC@Embed_Learn:~/Linux_System_Programming/Linux_file_programming/13_Standard_C_Library_fgetc_fputc_feof$ ./a.out 
fputc successful to file!

结束语

  很高兴您能看到这里,点个赞再走呗。谢谢您啦!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值