一、文件
什么是文件:
磁盘上的文件就是文件
1、以文件功能分类:
程序文件:
源程序文件test.c 目标文件test.obj 可执行程序test.exe
数据文件:
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要 从中读取数据的文件,或者输出内容的文件,如:data.txt
2、文件名
一个文件要有一个唯一的文件标识,以便用户识别和引用。
文件名包含3部分:文件路径+文件名主干+文件后缀
例如: c:\code\test.txt
为了方便起见,文件标识常被称为文件名。
二、文件指针
每一个打开的文件都会后一个与它匹配的文件信息区,存储文件信息
这些信息保存在一个结构体变量中,如:struct FILE f;。该结构体类型是有系统声明的,取名FILE.
例如,VS2008编译环境提供的 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结构的变量,并填充其中的信息,使用者不必关心细节。
一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。
三、文件的打开和关闭
1、fopen fclose
ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件。
2、“r” - 读
#include <stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
// 输出:
// fopen: No such file or directory
// 因为文件不存在
return -1;
}
// 读文件
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}
绝对路径
#include <stdio.h>
int main()
{
FILE* pf = fopen("C:\\Users\\Chloe\\Desktop\\data.txt", "r"); // 绝对路径
// FILE* pf = fopen("data.txt", "r"); // 相对路径
if (pf == NULL)
{
perror("fopen");
return -1;
}
// 读文件
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}
3、“w” - 写
#include <stdio.h>
int main()
{
FILE* pf = fopen("C:\\Users\\Chloe\\Desktop\\data.txt", "w"); // 会生成一个文件
if (pf == NULL)
{
perror("fopen");
return -1;
}
// 读文件
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}
四、顺序读写
1、有哪些函数
fputc - 字符输出函数 写一个字符
fgetc - 字符输入函数 读一个字符
2、fputc - 字符输出(写)
int fgetc( FILE *stream );
#include <stdio.h>
int main()
{
FILE* pf = fopen ("C:\\Users\\Chloe\\Desktop\\data.txt", "w");
if (NULL == pf)
{
perror("fopen");
return -1;
}
// 写文件
fputc('b', pf); // 在data.txt中写入bit
fputc('i', pf);
fputc('t', pf);
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}
2.1、所有输出流
流:高度抽象的概念
C语言的程序,只要运行起来,就默认打开了三个流:
stdout - 标准输出流
stdin - 标准输入流
stderr - 标准错误流
类型都是 FILE*
#include <stdio.h>
int main()
{
fputc('b', stdout);
fputc('i', stdout);
fputc('t', stdout);
}
3、fgetc - 字符输入(读)
int fputc( int c, FILE *stream );
3.1、从文件里读
把文件内容改成abcdef 会输出什么?
#include <stdio.h>
int main()
{
FILE* pf = fopen ("C:\\Users\\Chloe\\Desktop\\data.txt", "r");
if (NULL == pf)
{
perror("fopen");
return -1;
}
// 读文件
int ch = fgetc(pf);
printf("%c\n", ch); // a
ch = fgetc(pf);
printf("%c\n", ch); // b
ch = fgetc(pf);
printf("%c\n", ch); // c
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}
3.2、从标准输入读
也可以从标准输入读,也就是键盘
#include <stdio.h>
int main()
{
int ch = fgetc(stdin);
printf("%c\n", ch);
ch = fgetc(stdin);
printf("%c\n", ch);
ch = fgetc(stdin);
printf("%c\n", ch);
}
4、对比putchar printf getchar scanf
#include <stdio.h>
int main()
{
fputc('b', stdout);
// putchar printf("%c", ch);
int ch = fgetc(stdin);
// getchar scanf("%c", ch);
printf("%c\n", ch);
}
5、fputs - 文本输出(写)
int fputs( const char *string, FILE *stream );
把数据放进流里
#include <stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "w");
if (NULL == pf)
{
perror("fopen");
return -1;
}
// 写文件
// 写一行数据
fputs("hello world\n", pf);
fputs("hello bit\n", pf);
fclose(pf);
pf = NULL;
}
6、fgets - 文本输入(读)
char *fgets( char *string, int n, FILE *stream );
从流里读数据
使用示例:
想要读5个字符,但实际只读了4个字符,因为后面后有一个 \0
改成20:
7、fprintf - 格式化输出
int fprintf( FILE *stream, const char *format [, argument ]...);
与 printf 函数对比
示例:
#include <stdio.h>
struct S
{
int n;
double d;
};
int main()
{
struct S s = { 100, 3.14 };
FILE* pf = fopen("data.txt", "w");
if (NULL == pf)
{
perror("fopen");
return -1;
}
// 写文件
fprintf(pf, "%d %lf", s.n, s.d);
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}
8、fscanf - 格式化输入
int fscanf( FILE *stream, const char *format [, argument ]... );
与 printf 函数对比
示例:
#include <stdio.h>
struct S
{
int n;
double d;
};
int main()
{
struct S s = { 0 };
FILE* pf = fopen("data.txt", "r");
if (NULL == pf)
{
perror("fopen");
return -1;
}
// 读文件
fscanf(pf, "%d %lf", &(s.n), &(s.d));
printf("%d %lf", s.n, s.d);
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}
9、fwrite - 二进制输出
示例:
wb”(只写) 为了输出数据,打开一个二进制文件
#include <stdio.h>
struct S
{
int n;
double d;
char name[10];
};
int main()
{
struct S s = { 100, 3.24, "zhangsan"};
FILE* pf = fopen("data.txt", "wb");
if (NULL == pf)
{
perror("fopen");
return -1;
}
// 以二进制方式写
fwrite(&s, sizeof(s), 1, pf);
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}
文件内输出一堆二进制
10、fread - 二进制输入
示例:
#include <stdio.h>
struct S
{
int n;
double d;
char name[10];
};
int main()
{
struct S s = { 100, 3.24, "zhangsan" };
FILE* pf = fopen("data.txt", "rb");
if (NULL == pf)
{
perror("fopen");
return -1;
}
// 读文件 - 以二进制方式读
fwrite(&s, sizeof(s), 1, pf);
// 打印文件
printf("%d %lf %s\n", s.n, s.d, s.name); // 100 3.240000 zhangsan
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}
11、对比一组函数
scanf/fscanf/sscanf
printf/fprintf/sprintf
scanf:从标准输入流(键盘)读取格式化的数据
printf:把格式化的数据输出到标准输出(屏幕)上
fscanf:从所有的输入流读取格式化的数据
fprintf:把格式化的数据输出到所有输出流(屏幕 / 文件)上
剩下 sscanf 和 sprintf 是什么意思?
sscanf 与 sprintf
从一个字符串里读格式化的数据
写格式化的数据到字符串里
举例:
#include <stdio.h>
struct S
{
int n;
double d;
char name[10];
};
int main()
{
char arr[100] = { 0 };
struct S s = { 100, 3.14, "zhangsan" };
// 把一个格式化的数据转换成字符串
sprintf(arr, "%d %lf %s\n", s.n, s.d, s.name);
// 打印
printf("%s\n", arr);
return 0;
}
-
输出:
- 100 3.140000 zhangsan 由此可见 sprintf 的作用是:
- 把格式化的数据转换成对应的字符串
如何从arr里解析出格式化的结构体放进tmp:
#include <stdio.h>
struct S
{
int n;
double d;
char name[10];
};
int main()
{
char arr[100] = { 0 };
struct S s = { 100, 3.14, "zhangsan" };
struct S tmp = { 0 };
// 把一个格式化的数据转换成字符串
sprintf(arr, "%d %lf %s\n", s.n, s.d, s.name);
// 以字符串形式打印
printf("%s\n", arr); // 100 3.140000 zhangsan
// 从arr中的字符串中提取一个格式化的数据
sscanf(arr, "%d %lf %s", &(tmp.n), &(tmp.d), tmp.name);
// 按照格式化的形式打印的
printf("%d %lf %s\n", tmp.n, tmp.d, tmp.name); // 100 3.140000 zhangsan
return 0;
}
-
由此可见 sscanf 的作用是:
- 从字符串中读取一个格式化的数据(把一个字符串转换成一个格式化的数据)
优化通讯录程序
文件的版本:
【Contact】结构体+动态内存管理+文件存储实现简易通讯录代码