欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android NDK开发之C语言基础05:文件IO

程序员文章站 2022-06-06 17:06:45
...

一、文件的概念

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

二、文件的访问

(一)打开文件

使用函数:fopen()

使用方法:

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

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

Android NDK开发之C语言基础05:文件IO

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

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

一次能打开的文件数由

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。

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

Android NDK开发之C语言基础05:文件IO

四、读取文本文件

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’,在二进制模式下就没有意义了。

(二)二进制模式的优点

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

Android NDK开发之C语言基础05:文件IO

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

要指定二进制模式,只需要在基本打开模式说明符后附加 b
Android NDK开发之C语言基础05:文件IO
Android NDK开发之C语言基础05:文件IO

(四)写入二进制文件

使用函数:fwrite()
函数原型:
Android NDK开发之C语言基础05:文件IO
- 第一个参数是要写入的数组的地址(任何类型的数组都可以)
- 第二个参数是数组元素的字节数(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()
函数原型:
Android NDK开发之C语言基础05:文件IO

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

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

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


我的在行一点,欢迎大家扫码提问:
Android NDK开发之C语言基础05:文件IO