十四.文件操作

news2024/9/24 21:25:48

目录

一.为什么使用文件

二.什么是文件

1.程序文件和数据文件

2.文件名

三.文件的打开和关闭

1.文件指针

2.fopen函数和fclose函数

四.文件的顺序读写

1.顺序读写函数一览表

2.主要输入输出函数介绍

(1)字符输出函数futc

(2)字符输入函数fgetc

(3)文本行输出函数 fputs

(4)文本行输入函数 fgets

(5)格式化输出函数 fprintf

(6)格式化输入函数 fscanf

(7)二进制输出函数 fwrite

(8)二进制输入函数 fread

五.文件的随机读写

1.文件指针定位函数 fseek

2.返回偏移量函数 ftell

3.文件指针回到起始位置函数 rewind

六.流 的概念

七.文本文件和二进制文件

1.文本文件

2.二进制文件

3.数据在文件中的存储方式

八.文件读取结束的判定

1.经常被错误使用的“feof”函数

2.正确判定文件是否读取结束的方法

九.文件缓冲区

1.什么是文件缓冲区

2.冲刷缓冲区函数 fflush


一.为什么使用文件

引言:

  • 我们前面学习结构体时,写了通讯录的程序,当通讯录运行起来的时候,可以给通讯录中增加、删除数据,此时数据是存放在内存中,当程序退出的时候,通讯录中的数据自然就不存在了,等下次运行通讯录程序的时候,数据又得重新录入,如果使用这样的通讯录就很难受。
  • 我们在想既然是通讯录就应该把信息记录下来,只有我们自己选择删除数据的时候,数据才不复存在。这就涉及到了数据持久化的问题,我们一般数据持久化的方法有,把数据存放在磁盘文件、存放到数据库等方式。
  • 使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。
     

二.什么是文件

磁盘上的文件是文件。但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)

1.程序文件和数据文件

程序文件:

包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。

数据文件:

文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。 

2.文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用。

文件名包含3部分:文件路径+文件名主干+文件后缀

例如:c:\code\test.txt 

三.文件的打开和关闭

1.文件指针

在缓冲文件系统中,有一个关键的概念是 "文件类型指针" ,简称 "文件指针" 。每个被使用的文件,都会在内存中开辟出一个相应的文件信息区。该信息区用来存放文件相关信息(如文件名、文件状态以及文件当前位置等)。这些信息是保存在一个结构体变量中的,该结构体类型是由系统申明的,名为 FILE (注意是类型)。

例如由 VS2013 编译环境提供的 stdio.h 头文件中有以下的文件类型声明:

struct _iobuf {
    char *_ptr;
    int   _cnt;
    char *_base;
    int   _flag;
    int   _file;
    int   _charbuf;
    int   _bufsiz;
    char *_tmpfname;
};
typedef struct _iobuf FILE;

注:

  1. FILE 的结构在不同的C编辑器中包含的内容并不是不完全相同的,但还是颇为相似的。
  2. 每当打开一个文件时,系统会根据文件的状况自动创建一个 FILE 结构的变量,并填充其中的信 息,只要文件被读写发生变化,文件信息区也会跟着发生变化。至于文件变化时文件信息区是怎么变化和修改的,我们其实并不需要关心这些细节,因为C语言已经帮你弄好了。
  3. 我们一般会通过一个 FILE 的指针来维护这个 FILE 结构的变量。并不会直接使用,而是拿一个结构体指针指向这个结构,通过这个指针来访问和维护相关的数据,这样使用起来会更加方便。

下面我们来创建一个 FILE* 的指针变量:

FILE* pf;   //文件指针变量

定义 pf 是一个指向 FILE 类型的指针变量。可以使 pf 指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区的信息就能够访问该文件。 也就是说,通过文件指针变量能够找到与他关联的文件。

2.fopen函数和fclose函数

头文件:stdlib.h

ANSIC 规定使用 fopen 函数来打开文件, fclose 函数来关闭文件。

//打开文件
FILE * fopen ( const char * filename, const char * mode );

//关闭文件
int fclose ( FILE * stream );

打开方式如下:

文件使用方式含义如果指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)为了输出数据,打开一个文本文件建立一个新的文件
“a”(追加)向文本文件尾添加数据建立一个新的文件
“rb”(只读)为了输入数据,打开一个二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件建立一个新的文件
“ab”(追加)向一个二进制文件尾添加数据出错
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,建议一个新的文件建立一个新的文件
“a+”(读写)打开一个文件,在文件尾进行读写建立一个新的文件
“rb+”(读写)为了读和写打开一个二进制文件出错
“wb+”(读写)为了读和写,新建一个新的二进制文件建立一个新的文件
“ab+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件

用法演示:打开手动创建的 test.dat 文件

#include <stdio.h>
 
int main(void) {
    FILE* pf = fopen("test.dat", "w");
    
    //检查是否为空指针
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
 
    /* 写文件 */
 
    fclose(pf); //关闭文件
    pf = NULL;  //记得将pf置为空指针
 
    return 0;
}

运行结果:代码正常运行

现在我们把 test.dat 文件删除,然后按 r 的方式 打开文件

#include <stdio.h>
 
int main(void) {
    FILE* pf = fopen("test.dat", "r");
    
    //检查是否为空指针
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
 
    /* 写文件 */
 
    fclose(pf); //关闭文件
    pf = NULL;  //记得将pf置为空指针
 
    return 0;
}

运行结果:

四.文件的顺序读写

1.顺序读写函数一览表

功能函数名适用于
字符输入函数fgetc所有输入流
字符输出函数fputc所有输出流
文本行输入函数fgets所有输入流
文本行输出函数fputs所有输出流
格式化输入函数fscanf所有输入流
格式化输出函数fprintf所有输出流
二进制输入fread文件
二进制输出fwrite文件

2.主要输入输出函数介绍

(1)字符输出函数futc

int fputc(int char, FILE *stream)

将参数 char 指定的字符写入到指定的流 stream 中,并把位置标识符向前移动 (字符必须为一个无符号字符)。适用于所有输出流。

用法演示:

#include <stdio.h>
 
int main(void) {
    FILE* pf = fopen("test.txt", "w");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
 
    //写文件
    fputc('a', pf);
    fputc('b', pf);
    fputc('c', pf);
 
    //关闭文件
    fclose(pf);
    pf = NULL;
 
    return 0;
}

运行结果:

此时打开文件夹可以成功看到 test.txt 被创建了 ,并且里面写着 abc 三个字符。

(2)字符输入函数fgetc

int fgetc(FILE *stream)

从指定的流 stream 获取下一个字符,并把位置标识符向前移动(字符必须为一个无符号字符)。如果读取成功会返回相应的ASCII码值,如果读取失败它会返回一个EOF。适用于所有输入流。

用法演示:

提前在文件夹里新建 test.txt ,写入一些数据,随后使用 fgetc 函数读取并打印在屏幕上:

#include <stdio.h>

//使用fgetc从文件里读
int main(void) {
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    //读文件
    int ret = fgetc(pf);
    printf("%c\n", ret);
    ret = fgetc(pf);
    printf("%c\n", ret);
    ret = fgetc(pf);
    printf("%c\n", ret);
 
    //关闭文件
    fclose(pf);
    pf = NULL;
    
    return 0;
}

 运行结果:

(3)文本行输出函数 fputs

int fputs(const char *string, FILE *stream)

将字符串写入到指定的流 stream 中(不包括空字符)。适用于所有输出流。

用法演示:

#include <stdio.h>
 
int main(void) {
    FILE* pf = fopen("test2.txt", "w");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    //写文件 - 按照行来写
    fputs("abcdef\n", pf);
    fputs("123456\n", pf);
 
    //关闭文件
    fclose(pf);
    pf = NULL;
 
    return 0;
}

 运行结果:

(4)文本行输入函数 fgets

char *fgets(char *string, int n, FILE *stream)

从指定的流 stream 读取一行,并把它存储在 string 所指向的字符串中,当读取(n-1)个字符时,或者读取到换行符、到达文件末尾时,它会停止,具体视情况而定。适用于所有输入流。 

假如 n 是100,读取到的就是99个字符(n-1),因为要留一个字符给斜杠0。

用法演示:利用 fgets 读取先前建立的 test2.txt 中的内容:

#include <stdio.h>
 
int main(void) {
    char arr[10] = "xxxxxx"; //存放处
 
    FILE* pf = fopen("test2.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    //读文件 - 按照行来读
    fgets(arr, 4, pf);
    printf("%s\n", arr);
 
    fgets(arr, 4, pf);
    printf("%s\n", arr);
 
    //关闭文件
    fclose(pf);
    pf = NULL;
 
    return 0;
}

运行结果:
 

n 4,前 3 个是从文件中读取的,最后一个是 \0

打开调试窗口验证一下:

(5)格式化输出函数 fprintf

int fprintf(FILE *stream, const char *format, …………)

fprintf 用于对格式化的数据进行写文件,发送格式化输出到流 stream 中。适用于所有输出流。

用法演示:将结构体的三个数据利用 fprintf 写到 test3.txt 中:

#include <stdio.h>
 
struct Player {
    char name[10];
    int dpi;
    float sens;
};
 
int main(void) {
    struct Player p1 = { "carpe", 900, 3.12f };
 
    //对格式化的数据进行写文件
    FILE* pf = fopen("test3.txt", "w");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    
    //写文件
    fprintf(pf, "%s %d %f", p1.name, p1.dpi, p1.sens);
 
    // 关闭文件
    fclose(pf);
    pf = NULL;
 
    return 0;
}

运行结果:

(6)格式化输入函数 fscanf

int fscanf(FILE *stream, const char *format, …………)

fscanf 用于对格式化的数据进行读取,从流 stream 读取格式化输入。适用于所有输入流。

用法演示:利用 fscanf 读取 test3.txt 中的内容,并打印:

#include <stdio.h>
 
struct Player {
    char name[10];
    int dpi;
    float sens;
};
 
int main(void) {
    struct Player p1 = { 0 };  //存放处
 
    //对格式化的数据进行写文件
    FILE* pf = fopen("test3.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
 
    //读文件
    fscanf(pf, "%s %d %f",p1.name, &(p1.dpi), &(p1.sens) ); 
 
    //将读到的数据打印
    printf("%s %d %f\n", p1.name, p1.dpi, p1.sens);
 
    //关闭文件
    fclose(pf);
    pf = NULL;
 
    return 0;
}

运行结果:

(7)二进制输出函数 fwrite

size_t fread(const void* buffer, size_t size, size_t count, FILE *stream)

写一个数据到流中去,把 buffer 所指向的数组中的数据写入到给定流 stream 中。

用法演示:创建一个 test4.txt,用 fwrite 写入一个数据到 text4.txt 中去:

#include <stdio.h>
//二进制的形式写
 
struct S {
    char arr[10];
    int num;
    float score;
};
 
int main(void) {
    struct S s = { "abcde", 10, 5.5f };
 
    FILE* pf = fopen("test4.txt", "w");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    //写文件
    fwrite(&s, sizeof(struct S), 1, pf);
 
    //关闭文件
    fclose(pf);
    pf = NULL;
 
    return 0;
}

运行结果:

为什么是乱码?为什么 abcde 不是乱码?

  • 我们刚才用的都是文本编译器,文本编译器打开二进制形式的文件完全是两种状态。
  • 因为字符串以文本形式写进去和以二进制形式写进去是一样的,但是对于整数、浮点数等来说就不一样了,文本形式写入和二进制形式写入完全是两个概念。

(8)二进制输入函数 fread

size_t fread(void *buffer, size_t size, size_t count, FILE *stream)

从流中读取,从给定流 stream 读取数据到 buffer 所指向的数组中。

用法演示:用 fread 读取 text4.txt 中的二进制数据: 

#include <stdio.h>
//二进制的形式读
 
struct S {
    char arr[10];
    int num;
    float score;
};
 
int main(void) {
    struct S s = { 0 }; //存放处
 
    FILE* pf = fopen("test4.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    //读文件
    fread(&s, sizeof(struct S), 1, pf);
 
    //将读到的数据打印
    printf("%s %d %f", s.arr, s.num, s.score);
 
    //关闭文件
    fclose(pf);
    pf = NULL;
 
    return 0;
}

运行结果: 

五.文件的随机读写

1.文件指针定位函数 fseek

int fseek(FILE *stream, long offset, int origin)

根据文件指针的位置和偏移量来定位指针。

offset 是偏移量。

origin 是起始位置,有三种选项:

  1. SEEK_CUR  当前文件指针的位置开始偏移。
  2. SEEK_END  文件的末尾位置开始偏移。
  3. SEEK_SET  文件的起始位置开始偏移。

用法演示:创建一个文件,打开文件并随便写点内容

#include <stdio.h>
 
int main(void) {
    FILE* pf = fopen("test5.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
 
    //读取文件
    int ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);
 
    //关闭文件
    fclose(pf);
    pf = NULL;
 
    return 0;
}

运行结果: 

经过上一章节的介绍,这种读写方式为顺序读写,如果我们想得到 aab,该怎么做? 

我们可以试着使用 fseek 函数:

#include <stdio.h>
 
int main(void) {
    FILE* pf = fopen("test5.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    //读取文件
    int ch = fgetc(pf);
    printf("%c\n", ch);
 
    //调整文件指针
    fseek(pf, -1, SEEK_CUR); //SEEK_CUR为当前文件指针位置,偏移量为-1,向前移动1个单位
    ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);
 
    // 关闭文件
    fclose(pf);
    pf = NULL;
 
    return 0;
}

 运行结果:

尝试用 SEEK_SET ,打印 a d e: 

#include <stdio.h>
 
int main(void) {
    FILE* pf = fopen("test5.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
 
    //读取文件
    int ch = fgetc(pf);
    printf("%c\n", ch);
 
    //调整文件指针
    fseek(pf, 3, SEEK_SET); //SEEK_SET为文件的起始位置,偏移量为3,向后移动3个单位
    ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);
 
    //关闭文件
    fclose(pf);
    pf = NULL;
 
    return 0;
}

运行结果:

尝试用 SEEK_END ,打印 a e f

#include <stdio.h>
 
int main(void) {
    FILE* pf = fopen("test5.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
 
    //读取文件
    int ch = fgetc(pf);
    printf("%c\n", ch);
 
    //调整文件指针
    fseek(pf, -2, SEEK_END); //SEEK_END为当前文件末尾位置,偏移量为-2,向前移动2个单位
    ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);
 
    //关闭文件
    fclose(pf);
    pf = NULL;
 
    return 0;
}

运行结果:

2.返回偏移量函数 ftell

long ftell(FILE *stream) 

返回文件指针相对于起始位置的偏移量。

用法演示:

#include <stdio.h>
 
int main(void) {
    FILE* pf = fopen("test5.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
 
    //调整文件指针
    fseek(pf, 5, SEEK_CUR); // SEEK_CUR为当前文件指针位置,偏移量为5,向后移动5个单位
 
    //读取文件
    int ch = fgetc(pf);
    printf("%c\n", ch);  
 
    //返回偏移量
    int ret = ftell(pf);
    printf("%d\n", ret);  
 
    //关闭文件
    fclose(pf);
    pf = NULL;
 
    return 0;
}

运行结果:

3.文件指针回到起始位置函数 rewind

void rewind(FILE *stream)

rewind(意为倒带,磁带倒带),设置文件位置为给定流 stream 的文件的开头,让文件指针回到起始位置。

用法演示:

#include <stdio.h>
 
int main(void) {
    FILE* pf = fopen("test5.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
 
    //调整文件指针
    fseek(pf, 5, SEEK_CUR); //SEEK_CUR为当前文件指针位置,偏移量为5,向后移动5个单位
 
    //返回偏移量
    int loc = ftell(pf);
    printf("fseek调整文件指针后:%d\n", loc); 
 
    //让文件指针回到起始位置
    rewind(pf);
 
    //再次返回偏移量,看看是不是回到起始位置了
    loc = ftell(pf);
    printf("使用rewind后:%d\n", loc); 
 
    //关闭文件
    fclose(pf);
    pf = NULL;
 
    return 0;
}

运行结果:

由此可见,使用rewind后,文件指针返回到起始位置了 

六.流 的概念

在介绍文件的顺序读写函数时,观察表格我们可以发现有的函数是适用于所有xx流的。比如fputc函数就适用于所有输出流,也就是说它不仅仅可以给文件里写。

那么什么是流?

  • 我们假设写了一个程序,有时候想把数据信息打印到屏幕上,有时候想保存到硬盘里等,那么,该程序就要操作各种各样的硬件,这些硬件不同,它们的读写方式也不同,程序若想读写这些硬件就必须知到这些硬件的读写方式,这对于一个程序员来说要求和成本过高,这时候就有人提出了流的概念:
  • 在程序与各硬件之间抽象出一个层,叫做流(数据像水流一样),这个流再把相应的数据写入到不同的设备中,至于这个流是怎么向外部设备读写的,程序员不必关心。

C语言默认打开3个流:

  1. stdin   - 标准输入流 - 键盘
  2. stdout - 标准输出流 - 屏幕
  3. stderr  - 标准错误流 - 屏幕

演示:我们用流向屏幕上输出信息 - stdout:

#include <stdio.h>
 
int main(void) {
    fputc('a', stdout);
    fputc('b', stdout);
    fputc('c', stdout);
 
    return 0;
}

运行结果:abc

演示:fgetc 从标准输入流读取 - stdin

#include <stdio.h>
//使用fgetc从标准输入流中读
int main(void) {
    int ret = fgetc(stdin);
    printf("%c\n", ret);
    ret = fgetc(stdin);
    printf("%c\n", ret);
    ret = fgetc(stdin);
    printf("%c\n", ret);
    
    return 0;
}

运行结果:

七.文本文件和二进制文件

1.文本文件

如果要求在外存上以 ASCII 码的形式存储,则需要在存储之前进行转换。以 ASCII 字符的形式存储的文件,就是文本文件。

2.二进制文件

数据在内存中以二进制的形式存储,如果不加以转换地输出到外存,就是二进制文件。

3.数据在文件中的存储方式

一个数据在文件中是如何存储的呢?

有以下两条规则:

  • 字符一律以 ASCII 形式存储。
  • 数值型数据既可以用 ASCII 形式存储,也可以使用二进制形式存储

代码演示: 整数10000,如果以 ASCII 码的形式形式输出到磁盘,则磁盘中占用5个字节(每个字符占1个字节)。而如果以二进制的形式输出,则在磁盘上只占4个字节:

#include <stdio.h>
 
int main(void) {
    int a = 10000;
    FILE* pf = fopen("test6.txt", "wb");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    //写文件
    fwrite(&a, sizeof(int), 1, pf); //二进制的形式写到文件中
 
    //关闭文件
    fclose(pf);
    pf = NULL;
 
    return 0;
}

运行结果:

我们尝试在编辑器(vs2022)中打开二进制文本文件test6.txt:

 

我们来检查一下 10000 是不是 10 27 00 00:

文本文件和二进制文件的存储方式

  1. 文本文件:将内存里的数据转换成 ASCII 码值的形式存储到文件中。
  2. 二进制文件:将内存里的二进制数据不加任何转化直接存储到二进制文件中。

八.文件读取结束的判定

1.经常被错误使用的“feof”函数

int feof(FILE *stream)

正确用途:在文件结束时,判断文件因为何种原因导致文件结束的函数:判断是因为读取失败而结束,还是因为遇到文件尾而结束。如果文件结束,则返回非0值,否则返回0。 

错误用途:在文件读取过程中,不能用 feof 函数的返回值直接判断文件是否结束!feof 函数不是用来判断文件是否结束的函数,而是在文件已经结束时,判断是什么原因导致文件结束的。

用法演示:

#include <stdio.h>
#include <stdlib.h>
 
int main(void) {
    int ch = 0; 
    FILE* pf = fopen("test.txt", "r");
    if (!pf) {  //pf == NULL
        perror("fopen");
        return EXIT_FAILURE; //符号常量EXIT_FAILURE,表示没有成功地执行一个程序
    }

    //fgetc - 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
    while ( (ch = fgetc(pf)) != EOF ) {
        putchar(ch);
    } printf("\n");
 
    //判断文件结束的原因
    if (ferror(pf)) {  // ferror - 检查是否出现错误。
        puts("读取失败错误(I/O error when reading)");
    } else if (feof(pf)) {
        puts("遇到文件尾而结束(End of file reached successfully) ");
    }
 
    //文件关闭
    fclose(pf);
    pf = NULL;
}

运行结果:

2.正确判定文件是否读取结束的方法

文本文件读取是否结束,判断返回值是否为 EOFfgetc),或者 NULLfgets),例如:

  1. fgetc 函数在读取结束时会返回 EOF,正常读取时,返回读取到的字符的 ASCII 码值。
  2. fgets 函数在读取结束时会返回 NULL,正常读取时,返回存放字符串的空间的起始地址。
  3. fread 函数在读取结束时会返回 实际读取到的完整元素的个数,如果发现读取到的完整的元素个数小于指定的元素个数,那么就是最后一次读取了。

九.文件缓冲区

1.什么是文件缓冲区

ANSIC 标准采用缓冲文件系统来处理数据文件,所谓的缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块文件缓冲区。规则如下:

  1. 如果从内存向磁盘输出数据,会先送到内存中的缓冲区,缓冲区装满后再一起输送到磁盘上。
  2. 如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。
  3. 缓冲区的大小根据C编译系统决定的。

2.冲刷缓冲区函数 fflush

int fflush(FILE *stream)

强迫将缓冲区内的数据写回参数 stream 指定的文件中。刷新成功返回 0 ,如果发生错误则返回 EOF ,且设置错误标识符,即 feoffflush 不适用于高版本VS

用法演示:感受文件缓冲区的存在

#include <stdio.h>
#include <windows.h>
 
int main(void) {
    FILE* pf = fopen("test7.txt", "w");

    fputs("abcdef", pf);//先将代码放在输出缓冲区

    printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
    Sleep(10000);

    printf("刷新缓冲区\n");
    fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
    //注:fflush 在高版本的VS上不能使用了

    printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
    Sleep(10000);

    fclose(pf);
    //注:fclose在关闭文件的时候,也会刷新缓冲区
    pf = NULL;
 
    return 0;
}

运行结果如下:

 此时文本文件中没有数据,是因为此时数据暂时留存在文件缓冲区里。

 用 fflush 函数刷新缓冲区,此时文本文件中就有数据了。

总结:因为有缓冲区的存在,C语言在操作文件时,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。如果不做,可能导致读写文件的问题。


本篇到此结束,码文不易,还请多多支持哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/170942.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python采集*瓣电影影评并实现可视化分析

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ 环境使用: Python 3.8 解释器 Pycharm 编辑器 模块使用 import parsel >>> pip install parsel import requests >>> pip install requests import csv 安装python第三方…

LeetCode 1825 求出MK平均值【Set 队列】 HERODING的LeetCode之路

解题思路&#xff1a; 好久没更新力扣困难题的题解了&#xff0c;今天这道困难题有点意思&#xff0c;读罢题目一目了然&#xff0c;解题思路清晰明了&#xff0c;就是解题过程细节满满。这是一个数据流场景的问题&#xff0c;保留最后m个元素&#xff0c;但是要去除k个最大&am…

设计模式—工厂方法模式

工厂方法模式 文章目录工厂方法模式工厂方法模式是什么理解工厂方法模式代码实例运行截图工厂方法的优点工厂方法的不足工厂方法模式是什么 工厂方法模式属于创建型模式&#xff0c;也叫抽象构造模式&#xff0c; 工厂方法模式将工厂抽象化&#xff0c;并定义一个创建对象的接…

高级语言(C语言)、汇编语言、机器语言区别?编译器如何将高级语言编译成机器语言?

⾼级语⾔&#xff1a; 是相对于汇编语⾔⽽⾔的&#xff0c;是⾼度封装了的编程语⾔&#xff0c;与低级语⾔相对。它是以⼈类的⽇常语⾔为基础的⼀种编程语⾔&#xff0c;使⽤⼀般⼈易于接受的⽂字来表⽰&#xff08;例如汉字、不规则英⽂或其他外语&#xff09;&#xff0c;从…

(二十四)List系列集合

目录 前言: 一、List集合的特有方法 二、List集合的遍历方式有几种&#xff1f; 三、Arraylist集合底层原理 四、LinkedList的特点 前言: List集合包括JavaList接口以及List接口的所有实现类。List集合中的元素允许重复&#xff0c;各元素的顺序放是对象插入的顺序&#xff…

C生万物 | C语言文件操作指南汇总【内附文件外排序源码】

&#x1f451;作者主页&#xff1a;Fire_Cloud_1 &#x1f3e0;学习社区&#xff1a;烈火神盾 &#x1f517;专栏链接&#xff1a;万物之源——C 文章目录一、为什么使用文件&#xff1f;二、什么是文件&#xff1f;1、程序文件2、数据文件3、文件名三、文件的打开和关闭1、文件…

自动化测试【软件测试】

自动化测试 什么是自动化 有效减少人力的消耗&#xff0c;同时提高生活的质量 通过自动化测试有效减少人力的投入&#xff0c;同时提高了测试的质量和效率 由于回归测试&#xff0c;版本越来越多&#xff0c;版本回归的压力越来越大&#xff0c;仅仅通过人工测试来回归所有版本…

2.3、进程控制

整体框架 1、什么是进程控制&#xff1f; 进程控制的主要功能是对系统中的所有进程实施有效的管理&#xff0c; 它具有创建新进程、撤销已有进程、实现进程状态转换等功能。 简单来说&#xff1a;进程控制就是要实现进程状态转换 2、如何实现进程控制&#xff1f; 2.1、进程…

ATAC-seq分析:TSS 信号(7)

ATACseq ATACseq - 使用转座酶并提供一种同时从单个样本的转录因子结合位点和核小体位置提取信号的方法。 1. 数据类型 上面这意味着我们的数据中可能包含多种信号类型。 我们将从无核小体区域和转录因子&#xff08;我们的较短片段&#xff09;周围获得信号。我们的一部分信号…

2-Spring核心与设计思想

目录 1.Spring是什么&#xff1f; 2.容器是什么&#xff1f; 3.IoC是什么&#xff1f; 3.1.传统程序开发 3.2.控制反转式程序开发 3.3.对比总结规律 4.理解Spring IoC 4.1.将对象(Bean)存入到容器(Spring)&#xff1b; 4.2.从容器中取出对象。 5.DI概念说明 1.Spring…

计算机编程背景

&#x1f496; 欢迎来阅读子豪的博客&#xff08;JavaEE篇 &#x1f934;&#xff09; &#x1f449; 有宝贵的意见或建议可以在留言区留言 &#x1f4bb; 欢迎 素质三连 点赞 关注 收藏 &#x1f9d1;‍&#x1f680;码云仓库&#xff1a;补集王子的代码仓库 不要偷走我小火…

classpath类路径是什么

Spring Boot 一、简介 classpath类路径在 Spring Boot 中既指程序在打包前的/java/目录加上/resource目录&#xff0c;也指程序在打包后生成的/classes/目录。两者实际上指的是同一个目录&#xff0c;里面包含的文件内容一模一样。 二、获取classpath路径 以下两种方式均可…

Icarus Verilog

Icarus Verilog 是一个Verilog仿真工具&#xff0c;以编译器的形式工作&#xff0c;将以verilog编写的源代码编译为某种目标格式。如果要进行仿真的话&#xff0c;可以生成一个vvp的中间格式&#xff0c;由其所附带的vvp命令执行。 https://github.com/steveicarus/iverilog …

面试官:请设计一个能支撑百万连接的系统架构!

目录 1、到底什么是连接&#xff1f;2、为什么每次发送请求都要建立连接&#xff1f;3、长连接模式下需要耗费大量资源4、Kafka遇到的问题&#xff1a;应对大量客户端连接5、Kafka的架构实践&#xff1a;Reactor多路复用6、优化后的架构是如何支撑大量连接的 这篇文章&#x…

SQL Server 全文索引的应用

在公司项目中提出了一个需求&#xff1a; 搜索包含指定关键词的数据。得到这需求后&#xff0c;站在技术角度考虑第一时间就联想到使用SQL里面“like”查询语句。进一步分析需求后&#xff0c;发现“Like”查询满足不到实际的要求。 示例&#xff1a; ---------------------…

【Ajax】接口与接口测试工具PostMan

一、接口接口的概念使用 Ajax 请求数据时&#xff0c;被请求的 URL 地址&#xff0c;就叫做数据接口&#xff08;简称接口&#xff09;。同时&#xff0c;每个接口必须有请求方式。例如&#xff1a;http://www.liulongbin.top:3006/api/getbooks 获取图书列表的接口(GET请求)ht…

【4 - 降维算法PCA和SVD - 案例部分】菜菜sklearn机器学习

课程地址&#xff1a;《菜菜的机器学习sklearn课堂》_哔哩哔哩_bilibili 第一期&#xff1a;sklearn入门 & 决策树在sklearn中的实现第二期&#xff1a;随机森林在sklearn中的实现第三期&#xff1a;sklearn中的数据预处理和特征工程第四期&#xff1a;sklearn中的降维算法…

为何香港的IB状元特别多?

今年IB预科课程&#xff08;The International Baccalaureate Diploma Programme&#xff0c;IBDP&#xff09;公开考试放榜&#xff0c;香港的学校又是大丰收的一年&#xff01;因为香港今年一共有九十三名IB状元&#xff0c;即IB的总分为四十五分满分&#xff0c;而他们全部取…

Linux 环境部署 Nexus 服务

一 私服是什么&#xff1f; 一个特殊的远程仓库&#xff0c;它是架设在局域网内的仓库服务&#xff0c;供局域网内的开发人员使用。 当Maven需要下载构建的使用&#xff0c; 它先从私服请求&#xff0c;如果私服上没有的话&#xff0c;则从外部的远程仓库下载&#xff0c;然后…

算力服务亟待破局,超聚变向新而行

“超聚变已独立活下来。”超聚变产品线总裁范瑞琦在2023新品发布会上透露。自从一年多前从华为体系独立&#xff0c;超聚变公司的未来就颇受业界关注。一方面&#xff0c;算力产业蒸蒸日上&#xff0c;各方尤为关注超聚变的产业定位以及发展算力产业的着力点&#xff1b;另一方…