C语言文件操作
文件分类
在C程序设计中,一般将文件按功能分为程序文件和数据文件
程序文件
C程序文件包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行文
件(windows环境后缀为.exe)
数据文件
文件不一定是程序,而是程序运行时读写的数据,如程序运行需要从中读取数据或写
入数据的文件
二进制文件和文本文件
根据数据的组织形式,数据文件被称为文本文件或二进制文件
数据在内存中以二进制的形式存储,如果不加转换的输出到外存的文件中,就是二进
制文件
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的
形式存储的文件就是文本文件
字符一律以 ASCII 码存放,数值型数据既可以用 ASCII 码形式存储,也可以用二进制
存储

文件的打开和关闭
流
程序的数据需要和外部设备进行频繁的操作,为了方便对各种设备进行方便的操作,
抽象出了流的概念。
简单来说,流是连接程序和外部设备的管道,屏蔽了设备差异,流提供了统一的接口,
程序只需要调用这些接口就可以了
标准流
C语言程序启动时,默认打开了三个流:
stdin 标准输入流,在大多数的环境中从键盘输入,scanf函数就是从标准
输入流中读取数据
stdout 标准输出流,大多数的环境中输出至显示器界面,printf 函数就是
将信息输出到标准输出流中
stderr 标准错误流,大多数环境中输出到显示器界面。
stdin stdout stderr 三个流的类型是: FILE* , 通常称为文件指针
C语言中,就是通过 FILE* 的文件指针来维护流的各种操作
文件指针
缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使用
的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如
文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变
量中的。该结构体类型是由系统声明的,取名 FILE
例如在 visual studio 2013 中 stdio.h 头文件中有如图文件声明

不同的编译器的 FLIE 类型包含的内容不完全相同,大同小异
C程序打开一个文件时,系统会根据文件情况自动创建一个 FILE 结构的变量,并填充
其中的信息,定义一个 FILE* 类型的指针能够间接找到与它关联的文件

打开和关闭文件
文件在读写前应该先打开,使用结束后应该要关闭
程序在打开文件时会返回一个 FILE* 指针指向该文件
ANSI C 规定使用 fopen 打开文件 , fclose 关闭文件
语法格式
1 | FILE * fopen ( const char * filename, const char * mode ); |
mode 表示打开的模式,如图:
例如
1 | FILE* pf = fopen("66.txt", "r"); |
文件的顺序读写

演示 fputc
语法格式:
1 | int fputc ( int character, FILE * stream ); |
写入成功的返回值是写入的字符,写入失败返回 EOF
1 | FILE* pf = fopen("test1.txt", "w"); |

多次写入
1 | FILE* pf = fopen("test1.txt", "w"); |

有写入就有读取
1 | int fgetc ( FILE * stream ); |
读取成功后,会将读到的字符的 ASCII 码值返回
读取失败或读取到文件末尾返回 EOF
1 | FILE* pf = fopen("test1.txt", "r"); |

如果要写入或者读取字符串的话,要用 fputs 和 fgets
1 | int fputs ( const char * str, FILE * stream ); |
fputs 写入成功后返回非负值, 写入失败返回 EOF
fgets 中 str 用于存放读取到的字符串,num 用于指定最多读取多少个字符(包含
‘\0’), stream 表示从哪个文件读取
读取成功返回 str 的首元素地址,读取失败或读到文件末尾会返回 NULL
1 | FILE* pf = fopen("test2.txt", "w"); |

连同换行符也成功写入
1 | FILE* pf = fopen("test2.txt", "r"); |

可以看到 fgets 读取的时候是会给 ‘\0’ 留一个位置
如果这一行不够 fgets 读取会如何呢
1 | FILE* pf = fopen("test2.txt", "r"); |

fgets 不会换行读取
fgets 读取到文件尾会返回 NULL, 那想要完整读取这个文件可以这么写
1 | FILE* pf = fopen("test2.txt", "r"); |


fprintf / fscanf
fprintf 用于将数据按格式化形式打印到指定流
fscanf 用于从指定流上读入格式化数据
1 | int fprintf ( FILE * stream, const char * format, ... ); |
1 | FILE* pf = fopen("test3.txt", "w"); |

1 | FILE* pf = fopen("test3.txt", "r"); |

fprintf 和 fscanf 都是对指定数据流进行操作,stdout 是标准输入输出流, 就是说
fprintf 和 fscanf 能完成 printf 和 scanf 的功能
1 | //fscanf fprintf 都是对指定流进行操作 |
sprintf / sscanf
1 | int sprintf ( char * str, const char * format, ... ); |

sprintf 用于将格式化的数据写入字符串 str 中, sscanf 用于从字符串 s 中读取格式化的数据
1 | struct S |

fwrite / fread
1 | size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream ); |
fwrite 会将 ptr 指向的内存空间中的数据写入 count 个大小是 size 个字节到指定
数据流
fread 会从指定流中读取 count 个大小为 size 个字节的数据到 ptr 指向的
内存空间中
1 | //size_t fwrite(const void* ptr, size_t size, size_t count, FILE * stream); |

fread 读取成功时,会将成功读取的元素总个数返回,可以利用这个来完成数据流数
据的完整读取
1 | //size_t fread(void* ptr, size_t size, size_t count, FILE * stream); |

文件的随机读写
fseek
1 | int fseek ( FILE * stream, long int offset, int origin ); |
将文件指针从 origin 的位置移动 offset 个数据的位置, 正数是向后移动,负数是向前移动
这是 <stdio.h> 这个头文件中定义的 origin 的三个参数
现将文件 “test1.txt” 中放图中内容
1 | FILE* pf = fopen("test1.txt", "r"); |

ftell
1 | long int ftell ( FILE * stream ) |
返回当前文件指针相对于当前文件起始位置的偏移量
1 | FILE* pf = fopen("test1.txt", "r"); |

rewind
1 | void rewind ( FILE * stream ); |
设置文件指针到文件起始位置
1 | FILE* pf = fopen("test1.txt", "r"); |

文件结束的判定
feof
1 | int feof ( FILE * stream ); |
用于判断当文件读取结束时,是否是读到了文件末尾再结束
如果是,则返回非零值,否则返回 0
1 | FILE* pf = fopen("test1.txt", "r"); |

需要注意的是,不能用 feof 直接来判断文件是否读取结束
1 | FILE* pf = fopen("test1.txt", "r"); |

ferror
1 | int ferror ( FILE * stream ); |
如果文件操作发生了错误,返回非零值
如果没有发生错误,返回 0
1 | FILE* fp = fopen("test.txt", "r"); |



