打开文件

#include <stdlib.h>
exit(0);//头文件:#include <stdlib.h>
// exit 结束程序,一般0为正常退出,其他数字为异常,其对应的错误可以自己指定

FILE *fopen(文件名,使用方式);
//返回指向指定文件名的文件的指针;
FILE *fp;
fp=fopen("a1.txt","r");
使用文件方式
文件使用方式 含义 如果指定文件不存在
"r"(只读) 为了输入数据,打开一个已存在的文本文件 出错
"w"(只写) 为了输出数据,打开一个文本文件 建立新文件
"a"(追加) 向文本文件尾追加数据 出错
"rb"(只读) 为了输入数据,打开一个已存在的二进制文件 出错
"wb"(只写) 为了输出数据,打开一个二进制文件 建立新文件
"ab"(追加) 向二进制文件尾追加数据 出错
"r+"(读写) 为了读和写,打开一个文本文件 出错
"w+"(读写) 为了读和写,建立一个新的文本文件 建立新文件
"a+"(读写) 为了读和写,打开一个文本文件 出错
"rb+"(读写) 为了读和写,打开一个二进制文件 出错
"wb+"(读写) 为了读和写,建立一个新的二进制文件 建立新文件
"ab+"(读写) 为了读和写,打开一个二进制文件 出错
  • 用"r"方式:只能用于向计算机输入而不能用作向文件输出数据
  • 用"w"方式:只能用于向文件写数据,不能向计算机输入;(若文件不存在,则建立该文件;若文件存在,则先将该文件删除,然后重新建立新文件)
  • 用"a"方式:想文件末尾追加新的数据,不删除原有数据;(若文件不存在,则会出错;若文件存在,则打开文件,并将读写位置标记移至文件末尾)
fclose(文件指针);
//关闭文件用close函数;成功关闭返回0,否则返回EOF(-1)
fclose(fp);
  • 向文件写数据时,首先将数据输出到缓冲区,待缓冲区中充满后才正式输出给文件,如果数据未填满缓冲区而结束运行,就有可能使缓冲区中的数据丢失。
  • fclose函数:用fclose函数关闭文件,先把缓冲区中数据输出到磁盘文件,然后才撤销文件信息区

以字符形式读写

读写一个字符的函数
函数名 调用形式 功能 返回值
fgetc fgetc(fp) 从fp指向文件读入一个字符 读取成功,带会所读的字符,失败则返回文件结束标记EOF (即-1)
fputc fputc(ch,fp) 把字符ch写到文件指针变量fp所指向的文件 输入成功,返回值就是输入的字符;输入失败,返回EOF (即-1)

FILE *fp=fopen("hello.txt",""a+"); //打开文件 char ch=getc(fp);
(ch)!=EOF /** *每次读取一个字符,读取到( EOF:宏定义为-1 )结束 *fgetc()和getc的区别:fgetc()为函数实现;为getc()则为宏的实现 *宏避免了方法调用堆栈的操作,但不能传入内联函数(如 i++操作,可能使用之后得出错误结果) */ putc(ch,fp); //写入文件一个字符 ( fputc()/putc()的关系与fgetc()/getc()一样 ) fclose(fp);//关闭文件

以字符串形式读写

char * fgets(char *str,int n,FILE *fp);//从文件读入一个长度为(n-1)的字符串
int fputs(char *str,FILE *fp);//将str所指向的字符串输出到fp所指向的文件中
fputs("China",fp);
读写一个字符串的函数
函数名 调用形式 功能 返回值
fgets fgets(str,n,fp) 从fp指向文件读入一个长度为(n-1)的字符串,存放在字符数组str中 成功,返回str地址;否则返回NULL
fputs fputs(str,fp) 把str所指向的字符串写到文件指针变量fp所指向的文件中 成功,返回0;否则返回非0值
  • 写入文件:以“over”结束
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void main(){
    FILE *fp=fopen("G:\\111\\a.txt","w");
    char p[10];
    gets(p);
    while(strcmp(p,"over")){
        puts(p);
        fputs(p,fp);
        fputs("\n",fp);
        gets(p);
    }
    fclose(fp);
}
  • 读取文件:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void main(){
    FILE *fp=fopen("G:\\111\\a.txt","r");
    char p[10];
    while(!feof(fp)){
        fgets(p,11L,fp);
        printf("%s",p);
        //若使用puts打印字符串时会自动换行,破坏文件原本格式
    }
    fclose(fp);
}

格式化读写

  • 具体细节和printf函数和scanf函数一样,只是在第一个参数位置加入文件指针
int fprintf( FILE *stream,const char *format,... );
//优势:可以格式化写入,可以带一些变量
//如:fprintf( fp,"%d-%d-%d",year,mon,day );
int fscanf( FILE *stream,const char *format,... );
//优势:可以格式化读取,把读取到的部分内容赋值给一些变量
//如:fscanf( fp,"%d-%d-%d",&year,&mon,&day );

以二进制读写

size_t fwrite( void *ptr,size_t size,size_t nmemb,FILE *stream );
size_t fread( void *ptr,size_t size,size_t nmemb,FILE *stream );
/**
   *ptr:指向存放数据的指针,该内存的尺寸是 [size*nmenb] 个字节
   *size:指定要读取的每个元素的尺寸,最终尺寸为 [size*nmenb]
   *nmemb:指定读取的元素个数
   *stream:指定带读取的文件流
   *返回值:实际上读到的元素个数;如果该值小于 nmemb,就说明读取到文件末尾或有错误(可以用feof()或ferror()标志判断)
   */
  1.  打开模式字符串加了 'b',用以上方法可以以二进制形式读写;
  2. 用专门的二进制读写方法:这样就直接可以写入类型数据了;例如:整性数据、浮点型数据、结构体
  • 写入实例,二进制文件,直接打开是乱码
typedef struct {char name[20];double score;} Student;
void main(){
    FILE *fp=fopen("G:\\111\\a.txt","wb");
    Student stu[2]={"zhangshan",94,"lisi",96};
    int z;
    z=fwrite(stu,sizeof(Student),2,fp);
    printf("写入%d个数据\n",z);
    fclose(fp);
}
  • 读取案例:读取时,存放数据的指针必须指向有效地址,否则读取可能出错
typedef struct {char name[20];double score;} Student;
void main(){
    FILE *fp=fopen("G:\\111\\a.txt","rb");
    Student stu[2],*s=stu;
    int z;
    z=fread(s,sizeof(Student),2,fp);
    printf("读出入%d个数据\n",z);
    for(;z>0;z--){
        printf("%s:%.2f\n",stu[z-1].name,stu[z-1].score);
    }
    fclose(fp);
}

随机读写

  • 通过修改打开文件的位置指示器来达到直接读取目标位置的方法
  • 要注意将指示器位置移动至文件中间再写入数据时,会覆盖相应数据
long int ftell( FILE *stream );
/**
   *得到位置指示器的值;编译器认为文件的内容是一个字符数组
   *返回值:long 类型的值,返回值即为当前位置字符的下标
   */
rewint( FILE *stream );
/**
   *将位置指示器初始化到文件头位置
   *写入一些数据后,用该方法回到文件头位置继续写入会覆盖原有内容相应字节的内容
   */
int fseek( FILE *stream,long int offsef,int whence );
/**
   *用于设置文件流的位置指示器的位置
   *stream:指定代操作的文件流
   *offsef:指定从 whence 参数的位置器移动多少个字节(正数向后移动,负数向前移动)
   *whence:指定开始移动位置;可以用:
   *    0(SEEK_SET)【文件头】
   *    1(SEEK_CUR)【 当前的读写位置 】
   *    2(SEEK_END)【 文件末尾 】
   *返回值:成功返回0;失败返回非0值;如:fseek( fp,sizeof(student Stu)*1,0 );
   */

文件中的指示器

  • feof (fp):判断文件指示器位置是否在文件末尾
  • ferror (fp):判断文件指针是否发生错误
  • clearerr (fp):使文件错误标志和文件结束标志置为0
int feof( FILE *stream );
//位置指示器,读到文件末尾时返回非0值 int ferror( FILE *stream );
//错误指示器,文件读写发生错误时返回非0值(判断是否发生了错误) clearerr( FILE *stream );
//同时清除位置指示器和错误指示器的状态

程序执行时会打开3个面向终端的标准流

stdin; //标准输入(scanf函数接收数据)
stdout; //标准输出(printf函数打印字符串)
sederr; //标准错误输出(自动打印报错信息)
    //重定向:由于标准输出流和标准错误输出流都是直接将信息打印在终端;所以 Linux 可以通过重定向的方法将3个流的信息重定向到一个文件中
    //从定向标准输入:<    从定向标准输出:>    从定向标准错误输出:2>例如:
gcc test1.c && ./a.out     //(编译text1.c文件)
./a.out 2> error.txt    //(执行a.out,并将错误输出流重定向到 error.txt 中)
./a.out > output.txt     //(执行a.out,并将输出流重定向到 output.txt 中)
./a.out > 1.txt 2> 2.tet

IO缓冲区:(刷新缓冲区)

  • fflush( FILE *stream );手动将文件指针缓冲区的数据刷新入文件中
  • 缓冲区自动刷新
  1. 按块缓存:填满缓冲区后才进行实际的设备读写操作
  2. 按行缓存:接收到换行符之前,数据都是先缓存在缓冲区中的
  3. 不缓存:直接读写设备上的数据
int setvbuf( FILE *stream,char *buf,int mode,size_t size );
/**
   *设置缓存模式
   *stream:指定打开的数据流;
   *buf:传入字符数组,指定分配的缓冲区(为NULL时,函数会自动分配一个指定尺寸的缓冲区)
   *size:指定缓冲区的尺寸( 字节 );
   *mode:缓冲区模式
   *    _IOFBF(按块缓存)
   *    _IOLBF(按行缓存)
   *    _IONBF(不缓存)
   *返回值:调用成功返回0;调用失败返回非0值
   */