C语言基础05:文件IO

一、文件的概念

C语言的文件是指用来存储数据的一种存储设备。存储在文件中的数据并不会随着计算机的关闭而消失。文件通常是存储在硬盘上的。

二、文件的访问

(一)打开文件

使用函数:fopen()

使用方法:

char *path = "H:\\AndroidNDK\\cworkspace\\files\\friends.txt";
FILE *fp = fopen(path, "r");

函数的第一个变元是一个字符串指针,他是要处理的外部文件的名称。
函数的第二个变元是也是一个字符串,称为文件模式。它指定对文件进行什么处理。这里介绍常用的三种模式:

文件模式

如果我们需要处理的是一个二进制的文件,比如一个音频、视频或者图片时,那么文件模式为:"wb"、"ab"、"rb"。b 是 binary(二进制)的简写。

如果成功调用fopen()函数,它会返回一个FILE *类型的指针(该指针称为文件指针或者流指针),通过该指针可以引用文件,使用其他库函数执行输入输出的操作。如果文件因为某种原因但不开,会返回一个空指针(NULL)。

一次能打开的文件数由中定义的常量FOPEN_MAX确定。C语言标准规定,FOPEN_MAX的值至少为8,包括stdin、stdout和stderr。因此我们至少一次可以处理5个文件,但是这个数字常常大很多,例如256。

打开文件更安全的可选备用函数是:fopen_s():

errno_t fopen_s(_Outptr_result_maybenull_ FILE ** _File, _In_z_ const char * _Filename, _In_z_ const char * _Mode);
  • 第一个变量需要我们传入一个文件指针的地址
  • 第二个变量需要我们传入文件名(带路径或不带路径)
  • 第三个变量需要我们传入文件模式(w、a、r)

1.写入模式(w)

FILE *fp;
char *fileName = "H:\\AndroidNDK\\cworkspace\\hello.txt";
errno_t err = fopen_s(&fp, fileName, "w");
if (err != 0){
    printf("%s\n","文件打开失败");
    return;
}
printf("%s","请输入需要写入的字符(以#号结束):");
char str_append;
while ((str_append = getchar()) != '#'){
    fputc(str_append, fp);
}
fclose(fp);

对于第二个参数 fileName 可以写全路径

//如果包含这个文件的目录不存在,fopen_s()函数不会创建目录,也不会创建文件,而是失败。
//如果目录存在,但是该目录下没有找到文件,就在该位置上创建文件。
char *fileName = "H:\\AndroidNDK\\cworkspace\\hello.txt";

也可以不带路径,直接写文件名称:

//不带路径的话,该文件会保存到当前项目所在的目录下
char *fileName = "hello.txt";

注:如果fopen_s()函数调用失败,就返回非0整数,pfile设置为NULL。
使用fopen_s()函数对文件进行写入操作时,是不允许并发访问的。

2.追加模式(a)
如果要在已有的文本文件中添加数据,而不是覆盖数据,可以指定模式为“a”,它是操作的追加模式。将文件指针放在前一次写入的数据的末尾。如果文件不存在,就会创建新文件。

fopen_s(&fp, fileName, "a");

3.读取模式(r)
如果要读取文件,可以使用如下代码:

fopen_s(&fp, fileName, "r");

如果是读取模式,文件就必须存在,如果要读取的文件不存在,fopen_s()会把指针设置为NULL。

(二)缓存文件

打开文件后,就可以调用 setvbuf() 控制如何缓存输入操作,其函数原型如下:

int setvbuf(FILE * restrict pfile, char * restrict buffer, int mode, size_t size);
  • 参数一:打开文件的文件指针
  • 参数二:用于缓存的数组,第四个参数是该数组的大小。如果把第二个参数置为NULL,就用第四个变量指定的大小分配一个缓存。一般建议把第二个变量置为NULL,这样就不用考虑缓存的创建或其生命周期了。
  • 第三个参数:指定缓存模式。其值如下:
    _IOFBF:使文件完全缓存。输入输出完全缓存时,数据块会以任意大小读写。
    _IOLBF:使操作缓存一行。输入输出缓存一行时,读写的数据用换行符来分块。
    _IONBF:使用输入输出不缓存。对于不缓存的输入输出,数据会逐个字符地传递。这是非常低效的。所以仅在需要时使用这个模式。

一切正常时,setvbuf()返回 0 整数。示例代码:

//对pfile指向的文件使用setvbuf()
size_t bufsize = 1024;
if(setvbuf(pfile, NULL, _IOFBF, bufsize))
  printf("文件缓存失败!\n");

如果只是希望完全缓存输入或输出,可以调用 setbuf() ,其函数原型为:

void setbuf(FILE * restrict pfile, char * restrict buffer);
  • 第一个参数:文件指针
  • 第二个参数:用作缓冲区的数组地址。可以为NULL,此时自动创建缓冲区。如果指定缓冲区,其长度必须为
    BUFSIZ 字节,BUFSIZ在 stdio.h 中定义。下面使用自己的缓冲区给pfile指针指向的文件缓存操作:
char *buf = malloc(BUFSIZ);
setbuf(pfile,buf);

(三)重命名文件

使用函数 rename()
函数原型:

int rename(const char *oldname, const char *newname);

如果文件名称修改成功返回0,否则返回非零值。调用rename()函数时,文件必须关闭,否则操作会失败。
示例代码如下:

if (rename("H:\\cworkspace\\friends.txt", "H:\\cworkspace\\boy.txt")){
        printf("文件名修改失败\n");
    }
    else{
        printf("文件名修改成功\n");
    }

(四)关闭文件

使用函数:fclose()

fclose(fp);
fp=NULL;

如果成功关闭文件,就返回0,否则返回EOF。

注:EOF是一个特殊的字符,称为文件结束字符。EOF一般表示不能再从流中获取数据了
使用完文件后最好马上关闭文件

三、写入文本文件

最简单的写入操作由函数 fputc 提供,它将一个字符写入文本文件。其原型如下:

int fputc(int ch,FILE *pfile)

函数fputc()将第一个变量指定的字符写入第二个变量指定的文件中。如果操作成功,就返回写入的字符,否则就返回EOF。

注:实际上,字符不是一个一个地写入物理文件的,这样做效率太低了。字符先被写入到内存中的缓冲区,缓冲区累积到一定的数量后,就一次将它们写入文件。

写入文件.jpg

四、读取文本文件

fgetc() 函数从打开的文本文件中读取一个字符,需要传入一个文件指针对象作为参数,读取的字符返回为 int 类型,示例代码如下:

int ch = fgetc(pfile)

如果读取到文件末尾,就返回 EOF。读取文件的机制与写入文件正好相反。在一次操作中将一整块字符写入缓冲区,接着一次将一个字符传送给程序,直到缓冲区为空,此时再读取另一块。

如果需要再次读取文本内容,可以调用 rewind() 函数把文件指针变量指定的文件定位到开头。

rewind(pfile)

pfile必须是已经打开的文件。

五、在文本文件中读写字符串

(一)读取字符串

函数 fgets()

char *fgets(char * restrict str, int nchars, FILE * restrict pfile);

该函数会一直从文件中读取字符串,直到读到了'\n'字符或读入 nchars-1 个字符为止。如果读到换行符会保留在字符串中。字符'\0'会附加到字符串的末尾。如果没有错误,fgets()就会返回str指针,否则返回NULL。读取EOF会返回NULL。

(二)写入字符串

函数 fputs()

int *fputs(char * restrict str, FILE * restrict pfile);

第一个参数是要写入文件的字符串指针,第二个变量是文件指针。

fputs("hello world\n",pfile);

如果发生错误,fputs()函数返回EOF,如果正常就返回正整数。

六、二进制文件的输入输出

(一)二进制文件与文本文件的区别

1.二进制文件不需要转换数据,也不需要格式字符串控制输入输出;
2.文本模式下具有特殊意义的字符,如'\n'和‘\0’,在二进制模式下就没有意义了。

(二)二进制模式的优点

二进制模式的优点是没有数据转换,也没有精度的损失。而文本模式因为有格式化过程,有数据转换和精度损失。另外,二进制模式比文本模式的速度快。两种模式的比较如下图:

二进制模式与文本模式的比较

(三)以二进制模式打开文件

要指定二进制模式,只需要在基本打开模式说明符后附加 b

二进制文件操作的模式字符串

续表

(四)写入二进制文件

使用函数:fwrite()
函数原型:

fwrite()函数原型

  • 第一个参数是要写入的数组的地址(任何类型的数组都可以)
  • 第二个参数是数组元素的字节数(sizeof(数组类型))
  • 第三个参数是数组元素的个数
  • 第四个参数是文件流的指针

函数返回值:
函数fwrite()将实际写入的数据项个数返回为一个整数。如果出现写入错误,禁止写入所有数据,这个整数就小于nitems。如果size或nitems是0,就不给文件写入任何数据。

void main(){
    char *read_path = "H:\\AndroidNDK\\cworkspace\\files\\liuyan.png";
    char *write_path = "H:\\AndroidNDK\\cworkspace\\files\\liuyan_new.png";
    //读的文件
    FILE *read_fp = fopen(read_path, "rb");
    //写的文件
    FILE *write_fp = fopen(write_path, "wb");

    //复制
    int buff[50];//缓冲区域
    int len = 0;//每次读到的数据长度
    while ((len = fread(buff, sizeof(int), 50, read_fp)) != 0){
        fwrite(buff, sizeof(int), len, write_fp);
    }
    //关闭流
    fclose(read_fp);
        read_fp = NULL;
    fclose(write_fp);
        write_fp= NULL;
    getchar();
}

(五)读取二进制文件

使用函数:fread()
函数原型:

fread()函数原型

  • pdata是要读取的数组的地址
  • size是每个数据项的字节数
  • nitems是要读取的数据项个数
  • pfile是文件指针

函数的返回值:返回读取的个数。

关于文件IO的知识就给大家介绍到这里

你可能感兴趣的:(C语言基础05:文件IO)