「C/C++」标准文件操作大全

124 阅读9分钟

一、设备文件(运行程序时会默认打开这三个设备文件)

  • stdin:标准输入,默认为当前终端(键盘),我们使用的scanf、getchar函数默认从此终端获得数据。
  • stdout: 标准输出,默认为当前终端(屏幕),我们使用的printf、puts函数默认输出信息到此终端。
  • stderr:标准错误,默认为当前终端(屏幕),我们使用的perror函数默认输出信息到此终端

二、文件操作函数

1、perror(char strMsg):打印出错信息,参数是自己加的注释,为字符串类型

2、 FILE *fopen(const char *fileName,const char *mode)

  • 返回一个文件指针,并且打开路径为fileName的文件。失败返回NULL
  •    FILE *p = fopen("D:\Windows_lib\Desktop\test.txt", "a");
       if (p) {
           cout << "file open success"<<endl;
           cout<<fputc('c', p);
       } else {
           perror("fail to open file:");
       }
       fclose(fp);
    
打开模式(mode)含义
r或rb以只读方式打开一个文本文件(不创建文件,若文件不存在则报错)
w或wb以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件)
a或ab以追加方式打开文件,在末尾添加内容,若文件不存在则创建文件
r+或rb+以可读、可写的方式打开文件(不创建新文件,文件不存在则报错)
w+或wb+以可读、可写的方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件)
a+或ab+以添加方式打开文件,打开文件并在末尾更改文件,若文件不存在则创建文件

3、int fclose(FILE * stream)

  • 关闭文件,参数是要关闭的文件流指针

三、操作文件

1、int feof(FILE*stream)

  • EOF表示文件结束符(end of file),EOF是等于-1的
  • 检测是否读取到了文件结尾。判断的是最后一次“读操作的内容”,不是当前位置内容(上一个内容)
  • feof()读取完文件后返回0,没有读完则返回非0值
  •    while (!feof(fp)) //文件没有结束,则执行循环
       {
           char ch = fgetc(fp);
           printf("%c", ch);
       }
       fclose(fp);
    

2、int fputc(int ch , FILE* stream)

  • 将ch转换为unsigned char后写入stream指定的文件中
  • ch是需要写入文件的字符。
  • 成功返回写入文件的字符,失败返回-1
  •    FILE *p = fopen("D:\Windows_lib\Desktop\test.txt", "a");
       fputc('c', p);
       fclose(fp);
    

3、int fgetc(FILE *stream)

  • 从stream指定的文件中读取一个字符
  • 成功返回读取的字符,失败返回-1
  •    FILE *p = fopen("D:\Windows_lib\Desktop\test.txt", "r");
       cout<<(char)fgetc(p);
       fclose(fp);
    

4、int fputs(const char *str,FILE *stream)

  • 将str所指定的字符串写入到stream指定的文件中,字符串结束符 '\0' 不写入文件
  • 成功返回0,失败返回-1
  •    FILE *p = fopen("D:\Windows_lib\Desktop\test.txt", "a");
       cout<<fputs("\nthis is a msg\n", p);
       fclose(fp);
    

5、char *fgets(char *str,int size,FILE *stream)

  • 从stream指定的文件内读入字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 '\0' 作为字符串结束。通常用返回值判别是否读取完毕
  • 成功返回读取的字符串,读到文件为或出错返回NULL
  • 若要读取全部的文件内容,一般搭配while循环一直读取,直到返回NULL表明读取到了文件末尾
  •    FILE *p = fopen("D:\Windows_lib\Desktop\test.txt", "r");
       char buf[1024];
       if (p) {
           auto q = fgets(buf, sizeof buf, p);
           if(!q) cout<<"读取完毕";
           cout<<buf;
       }
       fclose(fp);
    

四、格式化文件操作

1、int fprintf(FILE *stream,const char *format,数据)

  • 根据参数format字符串来转换并格式化数据,然后将结果输出到stream指定的文件中,指定出现字符串结束符 '\0' 为止。
  • 类似sprintf,sscanf,将格式写入文件里
  • 成功返回实际写入文件的字符个数,失败返回-1
  •    int year=2023;
       int month=3;
       int day = 3;
       FILE *fp = fopen("D:\Windows_lib\Desktop\test.txt", "a");
       if (fp) {
           int t = fprintf(fp, "year:%d,month:%d,day:%d", year, month, day);
           if(t!=-1) puts("write successfully!");
       }
       fclose(fp);
    

2、int fscanf(FILE *stream,const char *format,数据)

  • 从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据。
  • 类似sscanf,sprintf,将格式读取出来
  • 成功则返回参数个数,失败返回-1
  •    int year, month, day;
       FILE *fp = fopen("D:\Windows_lib\Desktop\test.txt", "r");
       if (fp) {
           int t = fscanf(fp, "%d %d %d", &year, &month, &day);
           if(t!=-1) puts("read successfully!");
           cout<<year<<" "<<month<<" "<<day<<endl;
       }
       fclose(fp);
    

五、按照块读写文件

1、size_t fwrite(const void *ptr,size_t Size,size_t Count,FILE *stream)

  • 以数据块的方式给文件写入内容
  • ptr:源文件数据的地址
  • Size: 此参数指定写入文件内容的结构化数据大小
  • Count:写入文件的块数,写入文件数据总大小为:Size * nmemb
  • stream: 目的文件数据的地址(将被写入数据的文件)
  • 成功返回实际写入文件数据的字节数,和count值相等。失败返回0
  •   typedef struct {
          char id;
          char age;
          char name[20];
      } STD;
    
      STD xx = {'1', '2', "ssxaxas"};
    
      FILE *fp = fopen("D:\Windows_lib\Desktop\test.txt", "w");//"w"会清空文件,无法撤回
    
      if (fp) {
          int t = fwrite(&xx, sizeof xx, 1, fp);
          puts("Yes!");
        cout << t << endl;
      }
      fclose(fp);
    

2、size_t fread(void *ptr,size_t ElementSize,size_t Count,FILE *stream)

  • 以数据块的方式从文件中读取内容
  • ptr:存放读取出来数据的内存空间
  • ElementSize: size_t 为 unsigned int类型,此参数指定读取文件内容的结构化数据大小
  • Count:读取文件的块数,读取文件数据总大小为:ElementSize * nmemb
  • stream: 读取文件内容的文件指针
  • 成功返回实际写入文件数据的字节数,和count值相等。失败返回0
  •   typedef struct {
          char id;
          char age;
          char name[20];
      } STD;
    
      STD xx = {'1', '2', "ssxaxas"};
    
      FILE *fp = fopen("D:\Windows_lib\Desktop\test.txt", "r");
      if (fp) {
          int t = fread(&xx, sizeof xx, 1, fp);
          puts("Yes!");
          cout << xx.age << endl;
      } 
      fclose(fp);
    

六、文件的随机读写

1、int fseek(FILE *File,long Offset, int Origin)

  • 移动文件流(文件光标)的读写位置。

  • Offset:根据Origin来移动的位移数(偏移量),可以是正数,也可以负数,如果正数,则相对于Origin往右移动,如果是负数,则相对于Origin往左移动。如果向前移动的字节数超过了文件开头则出错返回,如果向后移动的字节数超过了文件末尾,再次写入时将增大文件尺寸。

  • 成功则返回0,否则返回非0值

  • Origin:其取值如下:

    SEEK_SET(0):从文件开头移动offset个字节

    SEEK_CUR(1):从当前位置移动offset个字节

    SEEK_END(2):从文件末尾移动offset个字节

  •   FILE *fp = fopen("D:\Windows_lib\Desktop\test.txt", "a");
      if (fp) {
          fputs("hello,world!",fp);
          fseek(fp,0,SEEK_SET);
          fputs("use_fseek-->",fp);
          // fp的内容应该是 use_fseek-->hello,world!
          puts("Yes!");
      }
      fclose(fp);
    

2、long ftell(FILE *stream)

  • 获取文件流(文件光标)的读写位置。一般用来测光标在文件的偏移位置
  • 成功返回当前文件流,文件光标的位置。失败返回-1

3、void rewind(FILE *stream)

  • 把文件流(文件光标)的读写位置移动到文件开头。

4、int stat(const char *path,struct stat *buf)

  • 获取文件状态信息
  • path:文件名
  • buf:保存文件信息的结构体,stat结构体如下,所需头文件如下
  • 成功返回0,失败返回-1
  •   struct stat {
      	dev_t         st_dev;         //文件的设备编号
      	ino_t         st_ino;          //节点
      	mode_t        st_mode;   //文件的类型和存取的权限
      	nlink_t       st_nlink;     //连到该文件的硬连接数目,刚建立的文件值为1
      	uid_t         st_uid;         //用户ID
      	gid_t         st_gid;         //组ID
      	dev_t         st_rdev;      //(设备类型)若此文件为设备文件,则为其设备编号
      	off_t         st_size;        //文件字节数(文件大小)
      	unsigned long st_blksize;   //块大小(文件系统的I/O 缓冲区大小)
      	unsigned long st_blocks;    //块数
      	time_t        st_atime;     //最后一次访问时间
      	time_t        st_mtime;    //最后一次修改时间
      	time_t        st_ctime;     //最后一次改变时间(指属性)
      };  
    
  •   #include <sys/types.h>	// 所包含的头文件
      #include <sys/stat.h>
    
      struct stat buf;	// 定义一个结构体对象来接受信息
    
      int ret = stat("D:\Windows_lib\Desktop\test.txt", &buf);
    
      if (ret == -1)
          puts("file not found!");
      else cout<<buf.st_size<<endl;	// 打印目标文件的字节数
    

七、文件的修改操作(均需要自己添上后缀名)

1、int remove(const char *pathname);

  • 删除路径为pathname的文件
  • 成功返回0,失败返回-1

2、int rename(const char *oldPath,const char *newPath)

  • 把oldpath的文件名改为newpath
  • 成功返回0,失败返回-1

八、Windows和Linux文本文件的区别

  • b是二进制模式的意思,b只是在Windows有效,在Linux用r和rb的结果是一样的
  • Unix和Linux下所有的文本文件行都是\n结尾,而Windows所有的文本文件行都是\r\n结尾
  • 在Windows平台下,以“文本”方式打开文件,不加b:
  • 当读取文件的时候,系统会将所有的 "\r\n" 转换成 "\n"
  • 当写入文件的时候,系统会将 "\n" 转换成 "\r\n" 写入
  • 以"二进制"方式打开文件,则读\写都不会进行这样的转换
  • 在Unix/Linux平台下,“文本”与“二进制”模式没有区别,"\r\n" 作为两个字符原样输入输出

九、文件缓冲区

ANSI C标准采用“缓冲文件系统”处理数据文件。

所谓缓冲文件系统是指系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区从内存向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去。

如果从磁盘向计算机读入数据,则一次从磁盘文件将一批数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(给程序变量) 。

文件缓冲区.png

  • 磁盘文件,一般保存在硬盘、U盘等掉电不丢失的磁盘设备中,在需要时调入内存
  • 在内存中对文件进行编辑处理后,保存到磁盘中
  • 程序与磁盘之间交互,不是立即完成,系统或程序可根据需要设置缓冲区,以提高存取效率

1、刷新缓冲区的3中方法

  • 缓冲区满
  • fflush函数强制刷新
  • 程序正常退出

2、int fflush(FILE *stream)

  • 更新缓冲区,让缓冲区的数据立马写到文件中。
  • 成功返回0,失败返回-1