世界杯积分榜_世界杯几年一届 - fjmzsy.com

16 为什么文件内容没写入?C 语言文件操作函数避坑 + 注意点,缓冲区机制讲清楚了

5431

⽂件操作

数据⽂件

⽂件的内容不⼀定是程序,⽽是程序运⾏时读写的数据,⽐如程序运⾏需要从中读取数据的⽂件,或 者输出内容的⽂件。其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使⽤,这⾥处理的就是磁盘上⽂件。

⽂件名

⼀个⽂件要有⼀个唯⼀的⽂件标识,以便⽤⼾识别和引⽤。⽂件名包含3部分:⽂件路径+⽂件名主⼲+⽂件后缀例如: c:\code\test.txt为了⽅便起⻅,⽂件标识常被称为⽂件名。

⼆进制⽂件和⽂本⽂件

根据数据的组织形式,数据⽂件被称为⽂本⽂件或者⼆进制⽂件。 数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存的⽂件中,就是⼆进制⽂件。 如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的⽂件就是⽂ 本⽂件。⼀个数据在⽂件中是怎么存储的呢字符⼀律以ASCII形式存储,数值型数据既可以⽤ASCII形式存储,也可以使⽤⼆进制形式存储。如有整数10000,如果以ASCII码的形式(即每个数字按照字符的格式)输出到磁盘,则磁盘中占⽤5个字节(每个字符⼀个字节),⽽⼆进制形式(这个数据在内存中占几个字节每个字节是多少一模一样搬到磁盘中)输出,则在磁盘上只占4个字节(VS2019测试)。

⽂件流和打开关闭

4.1.1 数据流

我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出 操作各不相同,为了⽅便程序员对各种设备进⾏⽅便的操作,我们抽象出了流的概念,我们可以把流 想象成流淌着字符的河。C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。 ⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。

4.1.2 标准流

那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢? 那是因为C语⾔程序在启动的时候,默认打开了3个流:

• stdin-标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。

• stdout-标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出 流中。

• stderr-标准错误流,⼤多数环境中输出到显⽰器界⾯。 这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。 stdin、stdout、stderr三个流的类型是: FILE* ,通常称为⽂件指针。 C语⾔中,就是通过 FILE* 的⽂件指针来维护流的各种操作的。

⽂件指针

缓冲⽂件系统中,关键的概念是“⽂件类型指针”,简称“⽂件指针”。 每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名 字,⽂件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系 统声明的,取名FILE.

struct _iobuf {

char *_ptr;

int _cnt;//引用计数--被几个进程或者线程打开

char *_base;

int _flag;

int _file;

int _charbuf;

int _bufsiz;

char *_tmpfname;

};

typedef struct _iobuf FILE;

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

⽂件的打开和关闭

⽂件在读写之前应该先打开⽂件,在使⽤结束之后应该关闭⽂件。 在编写程序的时候,在打开⽂件的同时,都会返回⼀个FILE*的指针变量指向该⽂件,也相当于建⽴了 指针和⽂件的关系。ANSIC规定使⽤ fopen 函数来打开⽂件, fclose 来关闭⽂件。

//打开⽂件-打开失败返回NULL

FILE * fopen ( const char * filename, const char * mode );

//关闭⽂件--关闭失败

int fclose ( FILE * stream );

mode表⽰⽂件的打开模式,下⾯都是⽂件的打开模式:

⽂件使⽤⽅式含义如果指定⽂件不存在“r”(只读)为了输⼊数据,打开⼀个已经存在的⽂本⽂件出错“w”(只写)为了输出数据,打开⼀个⽂本⽂件建⽴⼀个新的⽂件“a”(追加)向⽂本⽂件尾添加数据建⽴⼀个新的⽂件“rb”(只读)为了输⼊数据,打开一个⽂件以二进制方式读取出错“wb”(只写)为了输出数据,打开⼀个⽂件以二进制方式写入建⽴⼀个新的⽂件“ab”(追加)以二进制方式向⼀个⽂件尾添加数据建⽴⼀个新的⽂件

最佳实践打开方式--实例代码

不要以读写方式打开,避免造成读写数据混乱

/* fopen fclose example */

#include

int main ()

{

FILE * pFile;

//只写打开⽂件,如果给的不是绝对路径和相对路径那么默认在当前项目的目录下面新建文件

pFile = fopen ("myfile.txt","w");

//⽂件操作

if (pFile!=NULL)

{

//给文件以字符串形式(ascll)写入数据时,函数都是不写入字符串结尾的\0

fputs("ffffff", fptr);

fputs("ffffff", fptr);

fputs("ffffff\n", fptr);

fputs("ffffff", fptr);

fclose(fptr);

//关闭⽂件

fclose (pFile);

}

return 0;

}

文件路径及写入数据问题

问题1 --指定文件路径问题

如果使用fopen给的是绝对路径和相对路径如在windows下:

//相对路径--路径分隔符要使用双\\转义一下,因为单\本身就是转义字符

FILE* fptr = fopen("log\\t.txt", "w");

//绝对路径--路径分隔符要使用双\\转义一下,因为单\本身就是转义字符

FILE* fptr = fopen("D:\\Bit_learning\\Project 13\\log\\t.txt1", "w");

//在使用这两种路径的情况下确保中间的目录都存在,不然会打开失败

//因为fopen只能在已经存在的路径下新建文件

//只给文件名那么可以新建对应的文件

FILE* fptr = fopen("t.txt", "w");

if (fptr == NULL)

{

printf("fail\n");

return 1;

}

2 写入数据问题

1 给文件以字符串形式(ascll)写入数据时,函数都是不写入字符串结尾的\0

2二进制和文本模式的区别

1.在windows系统中,文本模式下,文件以"\r\n"代表换行。若以文本模式打开文件,并用fputs等函数写入换行符"\n"时,函数会自动在"\n"前面加上"\r"。即实际写入文件的是"\r\n" 。即打开文件查看时是一行一行的。以二进制写入时打开查看时没有换行符,所有字符都被以二进制的方式写入文件了即文件存储的是这个字符的ascll码值

2.在类Unix/Linux系统中文本模式下,文件以"\n"代表换行。所以Linux系统中在文本模式和二进制模式下并无区别

⽂件的顺序读写--具体使用查手册

函数名功能适⽤于fgetc字符输⼊函数所有输⼊流fputc字符输出函数fgets⽂本⾏输⼊函数fputs⽂本⾏输出函数fscanf格式化输⼊函数fprintf格式化输出函数fread⼆进制输⼊⽂件fwrite⼆进制输出⽂件

⽂件的随机读写

1 fseek

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

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

2 ftell

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

long int ftell ( FILE * stream );

3 rewind

//让⽂件指针的位置回到⽂件的起始位置

void rewind ( FILE * stream );

⽂件读取结束的判定

1.1 被错误使⽤的 feof

牢记:在⽂件读取过程中,不能⽤feof函数的返回值直接来判断⽂件的是否结束。 feof 的作⽤是:==当⽂件读取结束的时候,判断是读取结束的原因是否是:遇到⽂件尾结束。==

⽂本⽂件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets ) 例如: • fgetc 判断是否为 EOF . • fgets 判断返回值是否为 NULL .

⼆进制⽂件的读取结束判断,判断返回值是否⼩于实际要读的个数。 例如: • fread判断返回值是否⼩于实际要读的个数。

#include

#include

int main(void)

{

int c; // 注意:int,⾮char,要求处理EOF

FILE* fp = fopen("test.txt", "r");

if(!fp) {

perror("File opening failed");

return EXIT_FAILURE;

}

//fgetc 当读取失败的时候或者遇到⽂件结束的时候,都会返回EOF

while ((c = fgetc(fp)) != EOF) // 标准C I/O读取⽂件循环

{

putchar(c);

}

//判断是什么原因结束的

//ferror feof返回值

//当设置了与流关联的文件结束标识符时,该函数返回一个非零值,否则返回零

if (ferror(fp))//判断读取结束是不是因为读取出错

puts("I/O error when reading");

else if (feof(fp))//判断是不是正常读取结束

puts("End of file reached successfully");

fclose(fp);

}

⽂件缓冲区

ANSIC 标准采⽤“缓冲⽂件系统”处理的数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为 程序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓 冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输 ⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓 冲区的⼤⼩根据C编译系统决定的

#include

#include

//VS2019 WIN11环境测试

int main()

{

FILE*pf = fopen("test.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;

}

这⾥可以得出⼀个结论: 因为有缓冲区的存在,C语⾔在操作⽂件的时候,需要做刷新缓冲区或者在⽂件操作结束的时候关闭⽂ 件。如果不做,可能导致读写⽂件的问题.

缓冲区刷新策略

1对于标准输出来说-- 行刷新,输入到缓冲区的数据带有换行符(linux平台中,windows平台不用换行符也刷新)

2 缓冲区满了自动刷新

3 关闭文件描述符刷新

4 手动刷新

不朽之旅萌新兵装指南
8G内存显示3.4G的解决方法