0. 文件输入输出
使用printf()
和命令行重定向>
实现文件输出;使用scanf()
和命令行重定向<
实现文件输入。
- Hello.c
char name[256]; scanf("%s",name); printf("Hello %s\n",name);
- 编译
gcc Hello.c -o Hello
- 执行
Hello > Hi.txt Hello < Hi.txt Hello < Hi.txt > Hello.txt
- 练习
已知学生信息结构体如下:struct Student { char name[32]; //姓名 int age; //年龄 float score; //成绩 };
- 将一个学生信息写入文件并读出。
- 将多个学生信息写入文件并读出。
1. 文件打开关闭:fopen()
和:fclose()
1.1 打开文件:fopen()
- 函数原型
#include <stdio.h> FILE *fopen(char restrict *filename, char restrict *mode);
restrict C99标准才引进的,属于类型修饰符,表示修饰的这块内存空间只能被这个指针引用和修改,除此之外别无他法。
-
参数:
No. 参数 作用 1 filename
需要打开的文件 2 mode
文件打开方式 -
打开基本方式
No. 打开方式 含义 1 r
(read)读 2 w
(write)写 3 a
(append)追加 4 +
(plus)读或写,主要是配合r、w、a使用 5 t
(text)文本文件(默认) 6 b
(binary)二进制文件 -
打开组合方式
No. 打开方式 含义 1 r
以只读的方式打开文件,前提是这个文件必须存在(只写 r 默认是文本文件) 2 r+
以可读可写的方式打开文件,前提是这个文件必须存在(默认是文本文件)。 3 rb
以只读的方式打开一个二进制文件,前提是这个文件必须存在。 4 rb+
以可读可写的方式打开一个二进制文件,前提是这个文件必须存在。 5 w
以只写的方式打开文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则清空内容。 6 w+
以可读可写的方式打开文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则清空内容。 7 wb
以只写的方式打开一个二进制文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则清空内容。 8 wb+
以可读可写的方式打开一个二进制文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则清空内容。 9 a
以追加的方式打开只写文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则在文件尾部追加内容。 10 a+
以追加的方式打开一个可读可写的文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则在文件尾部追加内容。 11 ab
以追加的方式打开一个二进制只写文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则在文件尾部追加内容。 12 ab+
以追加的方式打开一个二进制可读可写文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则在文件尾部追加内容。
a
只能追加不能修改,w
会把文件清空,r+
读写方式可以修改文件原有内容。
- 返回值
如果文件顺利打开,则返回值是指向这个文件流的文件指针,如果文件打开失败,返回NULL
。
1.2 关闭文件:fclose()
- 函数原型
int flcose(FILE* stream);
- 参数
stream
文件指针 - 返回值
如果成功释放,返回0
, 否则返回EOF
(-1);
1.3 基本框架
一般来说,文件打开失败会做一个文件指针错误判断
FILE *fp = fopen("文件路径", "打开方式");
if(NULL != fp){
//code
fclose(fp);
}
2. 文本读写:fprintf()
和fscanf()
- 函数原型
int fprintf(FILE *stream, char *format, argument...); int fscanf(FILE *stream, char *format, argument... );
fprintf()
/fscanf()
与printf()
/scanf()
使用非常相似,区别在于fprintf()
/fscanf()
第一个参数stream
是文件描述符。 - 举例
将数据写入文件
从文件中读出数据int i = 10; float f = 3.14; char c = 'C'; char str[10] = "haha"; fprintf(fp, "%d %f %c %s\n", i, f, c, str);
int i = 10; float f = 3.14; char c = 'C'; char str[10] = "haha"; fscanf(fp, "%d %f %c %s\n", &i, &f, &c, str);
如果不需要从文件里面写入字符串,那么就可以用逗号或者其他符号来分隔;如果文件里需要写入字符串,那么字符串与其他数据之间只能用空格和回车来分隔。
- 练习
已知学生信息结构体如下:struct Student { char name[32]; //姓名 int age; //年龄 float score; //成绩 };
- 将一个学生信息写入文件并读出。
- 将多个学生信息写入文件并读出。
3. 二进制读写:fread()
和fwrite()
- 函数原型
size_t fread(void *ptr, size_t size, size_t count, FILE* stream); size_t fwrite(void *ptr, size_t size, size_t count, FILE* stream);
- 参数
No. 参数 作用 1 ptr
一个指针,在 fread()
中是从文件里读入的数据存放的地址;在fwrite()
中是写入到文件里的数据存放 的地址。2 size
每次要读写的字节数 3 count
读写的次数 4 stream
文件指针 - 返回值
成功读取/写入的字节数 - 举例
将字符串写入文件
从文件中读出字符串char str[] = "Hello World"; fwrite(str, sizeof(str), 1, fp);
char str[100]; fread(str, sizeof(str), 1, fp);
- 练习
已知学生信息结构体如下:struct Student { char name[32]; //姓名 int age; //年龄 float score; //成绩 };
- 将一个学生信息写入文件并读出。
- 将多个学生信息写入文件并读出。
查看二进制文件的命令
od -A x -t x1z -v 文件名
- 文本 vs 二进制
比较 文本 二进制 优势 便于人类读写,跨平台 文件较小,机器读写比较快 劣势 文件较大,机器读写比较慢 不便人类读写,不跨平台 配置 Unix用文件 Windows用注册表 - 说明
- Unix喜欢用文本文件来做数据存储和程序配置。
- windows喜欢用二进制文件。
- 数据量较多使用数据库
- 多媒体使用二进制
- 通常使用第三方库读写文件,很少直接读写二进制文件。
4. 文件定位:ftell()
和fseek()
-
函数原型
// 获取位置 long ftell(FILE* stream); // 设置位置 int fseek(FILE* stream,long offset,int whence);
-
参数
No. 参数 含义 1 stream
文件指针 2 offset
偏移量,基于起始点偏移了 offset
个字节3 whence
起始点 No. whence
数值 含义 1 SEEK_SET
0 从头开始 2 SEEK_CUR
1 从当前开始 3 SEEK_END
2 从结束开始 返回值
ftell()
返回文件指针当前位置,基于文件开头的偏移字节数。-
示例
fseek(stream, 0, SEEK_END); // 将文件指针指向文件结尾,并偏移了 0 个字节,也就是直接将文件指针指向文件结尾 fseek(stream, -10, SEEK_CUR); // 将文件指针指向当前位置,并偏移了 -10 个字节,也就是将文件指针往前移动10个字节
-
练习
获取文件大小char path[MAX_SIZE]; scanf("%s",path) FILE* fp = fopen(path,"r"); if(fp){ fseek(fp,0L,SEEK_END); long size = ftell(fp); printf("%s大小为%ldB\n",size); }
5. 文件结尾判断feof()
- 函数原型
int feof(FILE* stream);
- 参数
stream
文件指针 - 返回值
一旦文件指针指向文件结尾,就返回一个真值;否则返回非真值。
6. 返回开头rewind()
- 函数原型
void rewind(FILE* stream);
- 参数
stream
文件指针 - 举例
FILE *fp = fopen("./text.txt", "r+"); fseek(fp, 0, SEEK_END); // 将文件指针指向文件结尾 long len = ftell(fp); // 获取文件指针位置,得到文件的大小(Byte) rewind(fp); // 将文件指针重新指向文件开头
7. 清空数据流fflush()
- 函数原型
void fflush(FILE* stream);
- 参数
stream
数据流 - 举例
fflush(fp); // 清空文件流
8. 文件重命名rename()
和文件删除remove()
- 函数原型
int rename(const char *old_filename, const char *new_filename); int remove(char * filename);
实践
- 实现文件复制命令
cp
cp src dst
- 实现文件重命名命令
mv
mv src dst
项目
实现一个简单的注册登录系统miniLogin
- 支持注册用户名和密码,用户名和密码必须包含字母和数字。
- 登录时,密码输入错误超过3次,退出登录。