怎么做一款网站,花园之家wordpress,网站怎样做的高大上,商城网站建设框架一、什么是文件 磁盘上的文件是文件。 1、为什么要使用文件 举个例子#xff0c;当我们想实现一个 “通讯录” 程序时#xff0c;在通讯录中新建联系人、删除联系人等一系列操作#xff0c;此时的数据存储于内存中#xff0c;程序退出后所有数据都会随之消失。为了让通讯录…一、什么是文件 磁盘上的文件是文件。 1、为什么要使用文件 举个例子当我们想实现一个 “通讯录” 程序时在通讯录中新建联系人、删除联系人等一系列操作此时的数据存储于内存中程序退出后所有数据都会随之消失。为了让通讯录中的信息得以保存也就是想让数据持久化我们就需要采用让数据持久化的方法。我们一般数据持久化的方法有把数据存放在磁盘文件中或存放到数据库等方式。 在程序设计中我们一般谈的文件有两种程序文件、数据文件。
2、程序文件 包括源程序文件后缀为 .c 目标文件 windows 环境后缀为 .obj 可执行程序 windows 环境后缀为 .exe。 #include stdio.hint main()
{printf(Hello,World!\n);return 0;
} 1 2 3、数据文件 文件的内容不一定是程序而是程序运行时读写的数据比如程序运行需要从中读取数据的文件或者输出内容的文件。 此时如果我写的程序在读写这个文件我可以读这个文件夹的内容进我的程序中或是将一些内容写到这个文件夹中那么这个文件就成为数据文件。 二、文件名 一个文件要有一个唯一的文件标识方便用户识别和引用。 文件名包含三个部分文件路径 文件名主干 文件后缀 例如C:\code\test.txt 为了方便起见文件标识常被称为文件名。 三、文件类型
根据数据的组织形式数据文件被称为文本文件或者二进制文件。
数据在内存中以二进制的形式存储如果不加转换的输出到外存就是二进制文件。 如果要求在外存上以 ASCII 码的形式存储则需要在存储前转换。以 ASCII 字符的形式存储的文件就是文本文件。 一个数据在内存中是怎么存储的呢 字符一律以 ASCII 形式存储数值型数据既可以用 ASCII 形式存储也可以使用二进制形式存储。如有整数 10000 如果以 ASCII 码的形式输出到磁盘则磁盘中占用 5 个字节每个字符一个字节而二进制形式输出则在磁盘上只占 4 个字节 VS2019 测试。 #include stdio.hint main()
{int a 10000;FILE* pf fopen(test.txt, wb);fwrite(a, 4, 1, pf); // 二进制的形式写到文件中fclose(pf);pf NULL;return 0;
} 四、文件缓冲区File Buffer ANSIC 标准采用 “ 缓冲文件系统 ” 处理的数据文件的所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“ 文件缓冲区 ” 。从内存向磁盘输出数据会先送到内存中的缓冲区装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据则从磁盘文件中读取数据输入到内存缓冲区充满缓冲区然后再从缓冲区逐个地将数据送到程序数据区程序变量等。缓冲区的大小根据 C 编译系统决定的。 #include stdio.h
#include windows.hint main()
{FILE* pf fopen(test.txt, w);fputs(abcdef, pf); // 先将代码放在输出缓冲区printf(睡眠10秒-已经写数据了打开test.txt文件发现文件没有内容\n);Sleep(10000);printf(刷新缓冲区\n);fflush(pf); // 刷新缓冲区时才将输出缓冲区的数据写到文件磁盘printf(再睡眠10秒-此时再次打开test.txt文件文件有内容了\n);Sleep(10000);fclose(pf); // fclose在关闭文件的时候也会刷新缓冲区pf NULL;return 0;
} 结论 因为有缓冲区的存在C 语言在操作文件时需要做刷新缓冲区或者在文件操作结束的时候关闭文件。如果不做可能导致读写文件的问题。 五、文件指针 在 C 语言中用一个指针变量指向一个文件这个指针称为文件指针。通过文件指针就可对它所指的文件进行各种操作。 缓冲文件系统中关键的概念是“文件类型指针”简称“文件指针”。 每个被使用的文件都在内存中开辟了一个相应的文件信息区用来存放文件的相关信息如文件的名字文件状态及文件当前的位置等。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的取名 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; 注意 FILE 的结构在不同的 C 编辑器中包含的内容并不是不完全相同的但还是颇为相似的。每当打开一个文件时系统会根据文件的状况自动创建一个 FILE 结构的变量并填充其中的信息。只要文件被读写发生变化文件信息区也会跟着发生变化。至于文件变化时文件信息区是怎么变化和修改的我们其实并不需要关心这些细节因为 C 语言已经帮你弄好了。我们一般会通过一个 FILE 的指针来维护这个 FILE 结构的变量。并不会直接使用而是拿一个结构体指针指向这个结构通过这个指针来访问和维护相关的数据这样使用起来会更加方便。 // 以创建一个FILE*的指针变量
FILE* pf; // 文件指针变量 定义 pf 是一个指向 FILE 类型数据的指针变量。可以使 pf 指向某个文件的文件信息区是一个结构体变量。通过该文件信息区中的信息就能够访问该文件。也就是说通过文件指针变量能够找到与它关联的文件。 六、文件的打开和关闭 文件在读写之前应该先打开文件在使用结束之后应该关闭文件。 在编写程序的时候在打开文件的同时都会返回一个 FILE* 的指针变量指向该文件也相当于建立了指针和文件的关系。 ANSIC 规定使用 fopen 函数来打开文件fclose 来关闭文件。 // open the file
FILE * fopen (const char* filename, const char* mode);// close the file
int fclose (FILE* stream);// filename参数指的是文件名mode参数为打开方式 #include stdio.hint main()
{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.hint main()
{FILE* pf fopen(test.dat, r);if (pf NULL) // 检查是否为空指针{perror(fopen);return 1;}fclose(pf);pf NULL; // 将pf置为空指针return 0;
} perror 返回的错误信息。 如果不适用相对路径使用绝对路径读文件 可以使用绝对路径但是要注意转义绝对路径中的斜杠 #include stdio.hint main()
{// FILE* pf fopen(D:\code\0824\0824\test.dat, w); // errorFILE* pf fopen(D:\\code\\0824\\0824\\test.dat, w); // 转移字符\if (pf NULL){perror(fopen);return 1;}fclose(pf);pf NULL;return 0;
} 注意不关闭文件的后果一个程序能够打开的文件是有限的文件属于一种资源。如果只打开不释放文件就会被占用。可能会导致一些操作被缓冲在内存中如果不能正常关闭缓冲在内存中的数据就不能正常写入到文件中从而导致数据的丢失。 七、文件的顺序读写 顺序读写简而言之就是按照顺序在文件中读和写。 首先要了解什么是读写我们写的程序是在内存中而数据是要放到文件中的文件又是在硬盘上的。当我们把文件里的数据读到内存中去时这个动作我们称之为输入 / 读取。反过来如果把程序中的东西放到硬盘上这个动作我们称之为输出 / 写入。 1、什么是流
⚪stdin标准输入流 - 键盘
⚪stdout标准输出流 - 屏幕
⚪stderr标准错误流 - 屏幕
它们的类型都是 FILE*
#includestdio.hint main()
{// 从标准输出流里输入数据fputc(b, stdout);// 同printf(b);fputc(i, stdout);fputc(t, stdout);printf(\n);// 从标准输入流里读取数据int ret fgetc(stdin); // 同scanf(%d, ret);printf(%c\n, ret);ret fgetc(stdin);printf(%c\n, ret);ret fgetc(stdin);printf(%c\n, ret);return 0;
} 2、字符输入函数 fgetc
从指定的流 stream 获取下一个字符并把位置标识符向前移动字符必须为一个无符号字符。如果读取成功会返回相应的 ASCII码值如果读取失败会返回一个 EOF 。适用于所有输入流。 #include stdio.hint main()
{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;
} 读取结束或者读取失败返回 EOF。 3、字符输出函数 fputc
将参数 char 指定的字符写入到指定的流 stream 中并把位置标识符向前移动 字符必须为一个无符号字符。适用于所有输出流。 #include stdio.hint main()
{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;
} abc 成功被写入。 倘若将代码修改为
#include stdio.hint main()
{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;
} 此时再次运行我们发现那个文件里的内容不见了大小也变为0kb 4、文本行输入函数 fgets
从指定的流 stream 读取一行并把它存储在 string 所指向的字符串中当读取n-1个字符时或者读取到换行符、到达文件末尾时它会停止具体视情况而定。适用于所有输入流。 注意假如 n 是 100读取到的就是 99 个字符n-1因为要留一个字符给 \0。 #include stdio.hint main()
{char arr[10] xxxxxx; // 存放处FILE* pf fopen(test.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;
} 5、文本行输出函数 fputs
将字符串写入到指定的流 stream 中不包括空字符。适用于所有输出流。 #include stdio.hint main()
{FILE* pf fopen(test.txt, w);if (pf NULL){perror(fopen);return 1;}fputs(abcdef, pf);fputs(123456, pf);fclose(pf);pf NULL;return 0;
} 如果想要在 abcdef 后面换行只需在 fputs(abcdef\n, pf); 加上换行符即可。 6、 格式化输入函数 fscanf
fscanf 用于对格式化的数据进行读取从流 stream 读取格式化输入。适用于所有输入流。 #include stdio.hstruct Student
{char name[10];int id;float score;
};int main()
{struct Student s1 { 0 };// 对格式化的数据进行写文件FILE* pf fopen(test.txt, r);if (pf NULL){perror(fopen);return 1;}fscanf(pf, %s %d %f, s1.name, (s1.id), (s1.score));printf(%s %d %f\n, s1.name, s1.id, s1.score);fclose(pf);pf NULL;return 0;
} 注意 p1.name本身就是地址不用。 7、格式化输出函数 fprintf #includestdio.h
struct S
{char arr[10];int num;float sc;
};int main()
{struct S s { abcdef, 10, 5.5f };FILE* pf1 fopen(test.txt, w);if(pf1 NULL){perror(fopen);return 1;}fprintf(pf1, %s %d %f, s.arr, s.num, s.sc); // 将结构体格式化的数据写入文件fclose(pf1);pf1 NULL;struct S temp { 0 }; // 用于存储读出的数据 FILE* pf2 fopen(test.txt, r);if(pf2 NULL){perror(fopen);return 1;}fscanf(pf2, %s %d %f, temp.arr, (temp.num), (temp.sc)); // 再把数据从文件中读到结构体中printf(%s %d %f\n, temp.arr, temp.num, temp.sc); // 打印fclose(pf2);pf2 NULL;return 0;
}8、二进制输入函数 fread
从流中读取从给定流 stream 读取数据到 buffer 所指向的数组中。 #include stdio.h
// 二进制的形式读struct S
{char arr[10];int num;float score;
};int main()
{struct S s { 0 };FILE* pf fopen(test.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;
} 9、二进制输出函数 fwrite 写一个数据到流中去把 buffer 所指向的数组中的数据写入到给定流 stream 中。 #include stdio.h
// 二进制的形式写struct S
{char arr[10];int num;float score;
};int main()
{struct S s { abcde, 10, 5.5f };FILE* pf fopen(test.txt, w);if (pf NULL){perror(fopen);return 1;}fwrite(s, sizeof(struct S), 1, pf);fclose(pf);pf NULL;return 0;
} 为什么是乱码为什么 abcde 不是乱码 我们刚才用的都是文本编译器文本编译器打开二进制形式的文件完全是两种状态。因为字符串以文本形式写进去和以二进制形式写进去是一样的但是对于整数、浮点数等来说就不一样了文本形式写入和二进制形式写入完全是两个概念。 结论fwrite 和 fread 是一对fwrire 写进去用 fread 读。 八、文件的随机读写
1、文件指针定位函数 fseek
根据文件指针的位置和偏移量来定位文件指针。 int fseek (FILE* stream, long int offset, int origin);// offset是偏移量origin是起始位置有三种选项// 1、SEEK_CUR - 当前文件指针的位置开始偏移。
// 2、SEEK_END - 文件的末尾位置开始偏移。
// 3、SEEK_SET - 文件的起始位置开始偏移。 #include stdio.hint main()
{FILE* pf fopen(test.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;
} 如果我想得到 a a b该怎么处理呢
#include stdio.hint main()
{FILE* pf fopen(test.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.hint main()
{FILE* pf fopen(test.txt, r);if (pf NULL){perror(fopen);return 1;}int ch fgetc(pf);printf(%c\n, ch);// 调整文件指针fseek(pf, 2, SEEK_CUR); // SEEK_SET为文件的起始位置偏移量为2向后移动2个单位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.hint main()
{FILE* pf fopen(test.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 int ftell ( FILE * stream ); #include stdio.hint main()
{FILE* pf fopen(test.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); // f// 返回偏移量int ret ftell(pf);printf(%d\n, ret); // 6fclose(pf);pf NULL;return 0;
} 3、文件指针回到起始位置函数 rewind
rewind意为倒带磁带倒带设置文件位置为给定流 stream 的文件的开头让文件指针回到起始位置。 #include stdio.hint main()
{FILE* pf fopen(test.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); // 6// 让文件指针回到起始位置rewind(pf);// 再次返回偏移量看看是不是回到起始位置了loc ftell(pf);printf(使用rewind后%d\n, loc); // 6fclose(pf);pf NULL;return 0;
} 九、文件读取结束的判定
1、被错误使用的 feof
在文件结束时判断文件因为何种原因导致文件结束的函数判断是因为读取失败而结束还是因为遇到文件尾而结束。如果文件结束则返回非 0 值否则返回 0。 注意feof 函数是个经常被错误使用的一个函数。在文件读取过程中不能用 feof 函数的返回值直接判断文件是否结束feof 函数绝对不是用来判断文件是否结束的函数feof 不是用来判定文件是否结束了的还是在文件已经结束时判断是什么原因导致文件结束的。 ⚪文本文件读取是否结束判断返回值是否为EOF fgetc或者NULLfgets fgetc 判断是否为 EOF。fgets 判断返回值是否为 NULL。
#include stdio.h
#include stdlib.hint main()
{int ch 0; // 注意int非char要求处理EOFFILE* pf fopen(test.txt, r);if (!pf){perror(fopen);return EXIT_FAILURE; // 符号常量EXIT_FAILURE表示没有成功地执行一个程序}// fgetc - 当读取失败的时候或者遇到文件结束的时候都会返回EOFwhile ( (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;
} ⚪二进制文件的读取结束判断判断返回值是否小于实际要读的个数
fread 判断返回值是否小于实际要读的个数。
#include stdio.henum { SIZE 5 };int main(void)
{double a[SIZE] {1.0,2.0,3.0,4.0,5.0};double b 0.0;size_t ret_code 0;FILE *fp fopen(test.bin, wb); // 必须用二进制模式fwrite(a, sizeof(*a), SIZE, fp); // 写double的数组fclose(fp);fp fopen(test.bin,rb);// 读 double 的数组while((ret_code fread(b, sizeof(double), 1, fp))1){printf(%lf\n,b);}if (feof(fp)){printf(Error reading test.bin: unexpected end of file\n);}else if (ferror(fp)){perror(Error reading test.bin);}fclose(fp);fp NULL;
} 2、正确判定文件是否读取结束的方法
文本文件读取是否结束判断返回值是否为 EOFfgetc或者 NULLfgets。 fgetc 函数在读取结束时会返回 EOF正常读取时返回读取到的字符的 ASCII 码值。fgets 函数在读取结束时会返回 NULL正常读取时返回存放字符串的空间的起始地址。fread 函数在读取结束时会返回实际读取到的完整元素的个数如果发现读取到的完整的元素个数小于指定的元素个数那么就是最后一次读取了。