文件流
文件流概念
C 语言把文件看作是一个字符的序列,即文件是由一个一个字符组成的字符流,因
此 c 语言将文件也称之为文件流。即,当读写一个文件时,可以不必关心文件的格式或
结构。
文件类型
文件分类
大家都知道计算机的存储,物理上是二进制的,所以文本文件与二进制文件的区
别并不是物理上的,而是逻辑上的。这两者只是在编码层次上有差异。简单来说,文
本文件是基于字符编码的文件,常见的编码有 ASCII 编码,二进制文件是基于值编码
的文件。
文本文件:以 ASCII 码格式存放,一个字节存放一个字符。 文本文件的每一个
字节存放一个 ASCII 码,代表一个字符。这便于对字符的逐个处理,但占用存储空间
较多,而且要花费时间转换。
二进制文件:以值(补码)编码格式存放。二进制文件是把数据以二进制数的格
式存放在文件中的,其占用存储空间较少。数据按其内存中的存储形式原样存放。
#include <stdio.h>
int main()
{
//short a = 10000;
//FILE * fp = fopen("ascii.txt", "w");
//fprintf(fp, "%d", a);
//fclose(fp);
//FILE *fp2 = fopen("bin.txt", "w");
//fwrite(&a, 2, 1, fp2);
//fclose(fp2);
//花费时间用于转化
//占用磁盘空间多,磁盘中的文件,易读
char *buf = "abcdefg";
FILE* fp = fopen("ascii2.txt", "w");
fprintf(fp, "%s", buf);
fclose(fp);
//不需要花费时间转化
//相比于文本,占用磁盘空间小,磁盘文件不易读
FILE * fp2 = fopen("bin2.txt", "w");
fwrite(buf, 8, 1, fp2);
fclose(fp2);
return 0;
}
乱码原由
举例文本工具,打开文件的过程。拿记事本来说,它首先读取文件物理上所对应
的二进制比特流,然后按照你所选择的解码方式来解释这个流,然后将解释结果显示
出来。一般来说,你选取的解码方式会是 ASCII 码形式(ASCII 码的一个字符是 8 个比
特),接下来,每 8 个比特 8 个比特地来解释这个文件流。
例如文件流 01000000_01000001_01000010_01000011"(下划线''_'',为了增强可读
性手动添加的),第一个 8 比特''01000000''按 ASCII 码来解码的话,所对应的字符是字
符''A'',同理其它 3 个 8 比特可分别解码为''BCD'',即这个文件流可解释成"ABCD",
然后记事本就将这个"ABCD"显示在屏幕上。
记事本无论打开什么文件都按既定的字符编码工作(如 ASCII 码),所以当他打
开二进制文件时,出现乱码也是很必然的一件事情了,解码和译码不对应嘛。
例如文件流''00000000_00000000_00000000_00000001''可能在二进制文件中对
应的是一个四字节的整数 int 1,在记事本里解释就变成了"NULL_NULL_NULL_SOH"这
四个控制符。
文件缓冲
为什么要有缓冲区(buffer) 原因为多种,有两个重点:
1.从内存中读取数据比从文件中读取数据要快得多。
2.对文件的读写需要用到 open、read、write 等系统底层函数,而用户进程每调用
一次系统函数都要从用户态切换到内核态,等执行完毕后再返回用户态,这种切
换要花费一定时间成本(对于高并发程序而言,这种状态的切换会影响到程序性
能)。
举个例子,如果程序需要处理 10K 个整数(或者 10K 个字符等等),而这些整数
事先存在某个文件中,如果程序每处理一个整数就要从文件中读一个整数(read 系统
调用),那么每次都要进行硬件 I/O、进程状态切换等操作,这样效率是非常低下的。
如果每次从文件中读出 1K 个整数到内存,程序从内存中读取数据并处理,那么程序的
性能会明显提高,存储这 1K 个整数的内存区域就是一个缓冲区。
#include <stdio.h> // linux code only
int main()
{
while (1)
{
printf("abcdefg"); //缓冲区满,则会写入文件。
usleep(10000);
}
return 0; // 也可以通过 fclose 和 fflush 刷缓冲的
}
文件的打开和关闭
FILE 结构体
在 C 语言中,文件操作都通过 FILE 结构体来实现的。FILE 结构体定义如下:
typedef struct {
short level; /* 缓冲区满/空程度 */
unsigned flags; /* 文件状态标志 */
char fd; /* 文件描述符 */
unsigned char hold; /* 若无缓冲区不读取字符 */
short bsize; /* 缓冲区大小 */
unsigned char *buffer; /* 数据传送缓冲区位置 */
unsigned char *curp; /* 当前读写位置 */
unsigned istemp; /* 临时文件指示 */
short token; /* 用作无效检测 */
} FILE ; /* 结构体类型名 FILE */
在开始执行程序的时候,将自动打开 3 个文件和相关的流:标准输入流(stdin)、标
准输出流(stdout)和标准错误(stderr),它们都是 FIEL*型的指针。流提供了文件和程序的
通信通道。
fopen
函数原型:
FILE *fopen(const char *filename, const char *mode);
功能:以 mode 的方式,打开一个 filename 命名的文件,返回一个指向该文件
缓冲的 FILE 结构体指针。
参数:
- filename:要打开的文件名。
- mode:打开文件的方式。
返回值:成功打开文件,返回一个指向 FILE 结构体的指针;失败返回 NULL。
mode参数:
- r:以只读方式打开文件。
- w:以写方式打开文件,若文件不存在则创建文件,若文件存在则清空文件内容。
- a:以追加方式打开文件,若文件不存在则创建文件,若文件存在则从文件尾部开始写。
- r+:以读写方式打开文件。
- w+:以读写方式打开文件,若文件不存在则创建文件,若文件存在则清空文件内容。
- a+:以读写方式打开文件,若文件不存在则创建文件,若文件存在则从文件尾部开始写。
- rb:以二进制只读方式打开文件。
- wb:以二进制写方式打开文件,若文件不存在则创建文件,若文件存在则清空文件内容。
- ab:以二进制追加方式打开文件,若文件不存在则创建文件,若文件存在则从文件尾部开始写。
- rb+:以二进制读写方式打开文件。
- wb+:以二进制读写方式打开文件,若文件不存在则创建文件,若文件存在则清空文件内容。
- ab+:以二进制读写方式打开文件,若文件不存在则创建文件,若文件存在则从文件尾部开始写。
fclose
函数原型:
int fclose(FILE *fp);
功能:fclose()用来关闭先前 fopen()打开的文件. 此动作会让缓冲区内的
数据写入文件中, 并释放系统所提供的文件资源.
参数:
- fp:一个指向 FILE 结构体的指针。
返回值:成功返回 0;失败返回 EOF。
一次读写一个字符(文本操作)
fputc
函数原型:
int fputc(int c, FILE *fp);
功能:fputc()函数向文件中写入一个字符。
参数:
- c:要写入的字符。
- fp:一个指向 FILE 结构体的指针。
返回值:成功返回写入的字符值;失败返回 EOF。
#include <stdio.h>
int main()
{
FILE* fp = fopen("ascii.txt","w");
if(fp == NULL)
{
printf("open error\n");
return -1