1. 什么是文件
磁盘上的文件是文件。 但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。
1.1 程序文件
包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境 后缀为.exe)。
1.2 数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件, 或者输出内容的文件。
1.3 文件名
1.4 文件缓冲区
2. 文件的打开和关闭
2 .1 文件指针:FILE
缓冲文件系统中,关键的概念是"文件类型指针",简称"文件指针"。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名 字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统 声明的,取名FILE.
一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。
下面我们可以创建一个FILE*的指针变量:
FILE* pf;//文件指针变量
3.2 文件的打开(fopen)和关闭(fclose)
文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指 针和文件的关系。
ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件
//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream ); 比特
cpp
#include <errno.h>
#include <string.h>
int main()
{
//打开文件test.txt
//相对路径
//.. 表示上一级路径
//. 表示当前路径
//fopen("../../test.txt", "r")
//fopen("test.txt", "r");
//绝对路径的写法【添加转义字符】
//fopen("C:\\2020_code\\84班\\test_5_6\\test_5_6\\test.txt", "r");
FILE* pf = fopen("test.txt", "w");
if(pf == NULL)
{
printf("%s\n", strerror(errno));
return 0;
}
//打开成功
//读文件
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
3. 文件的顺序读写
3.1 fputc:写入文件
操作一行
cpp
int main()
{
FILE* pfWrite = fopen("TEST.txt", "w");
if(pfWrite == NULL)
{
printf("%s\n", strerror(errno));
return 0;
}
//写文件
fputc('b', pfWrite);
fputc('i', pfWrite);
fputc('t', pfWrite);
//关闭文件
fclose(pfWrite);
pfWrite = NULL;
return 0;
}
3.2 fgetc():读取文件
操作一行
cpp
int main()
{
FILE* pfRead = fopen("test.txt", "r");
if(pfRead == NULL)
{
printf("%s\n", strerror(errno));
return 0;
}
//读文件
printf("%c", fgetc(pfRead));//b
printf("%c", fgetc(pfRead));//i
printf("%c", fgetc(pfRead));//t
//关闭文件
fclose(pfRead);
pfRead = NULL;
return 0;
}
3.3 输入/输出设备
从键盘输入
输出到屏幕.
键盘&屏幕都是外部设备【默认打开的】
键盘-标准输入设备- stdin
屏幕-标准输出设备- stdout
是一个程序默认打开的两个流设备
stdin FILE*
stdout FILE*
stderr FILE*
cpp
int main()
{
int ch = fgetc(stdin);
fputc(ch, stdout);
return 0;
}
3.4 fgets():读取文件存储到指定位置
操作多行
cpp
int main()
{
char buf[1024] = { 0 };
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
return 0;
}
//读文件
//表示从pf输入流中读取1024个字节到buf中
fgets(buf, 1024, pf);
printf("%s", buf);//bit
return 0;
}
3.5 fputs()
要手动换行
cpp
int main()
{
char buf[1024] = { 0 };
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
return 0;
}
//写文件
fputs("hello\n", pf);
fputs("world\n", pf);
fclose(pf);
pf = NULL;
return 0;
}
3.6 fputs()与fgets()
cpp
int main()
{
//从键盘读取一行文本信息
//char buf[1024] = {0};
fgets(buf, 1024, stdin);//从标准输入流读取
fputs(buf, stdout);//输出到标准输出流
//gets(buf);
//puts(buf);
return 0;
}
3.7 puts()
会自动换行
3.8 fprintf()
cpp
struct S
{
int n;
float score;
char arr[10];
};
int main()
{
struct S s = { 100, 3.14f, "bit" };
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
return 0;
}
//格式化的形式写文件
fprintf(pf, "%d %f %s", s.n, s.score, s.arr);
fclose(pf);
pf = NULL;
return 0;
}
3.9 fscanf()
cpp
struct S
{
int n;
float score;
char arr[10];
};
int main()
{
struct S s = {0};
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
return 0;
}
//格式化的输入数据
fscanf(pf, "%d %f %s", &(s.n), &(s.score), s.arr);
printf("%d %f %s\n", s.n, s.score, s.arr);
fclose(pf);
pf = NULL;
return 0;
}
3.10 fscanf()和fprintf()
cpp
struct S
{
int n;
float score;
char arr[10];
};
int main()
{
struct S s = { 0 };
fscanf(stdin, "%d %f %s", &(s.n), &(s.score), s.arr);
fprintf(stdout, "%d %.2f %s", s.n, s.score, s.arr);
return 0;
}
3.11 面试题
cpp
int main()
{
struct S s = { 100, 3.14f, "abcdef" };
struct S tmp = {0};
char buf[1024] = { 0 };
//把格式化的数据转换成字符串存储到buf
sprintf(buf, "%d %f %s", s.n, s.score, s.arr);
//printf("%s\n", buf);
//从buf中读取格式化的数据到tmp中
sscanf(buf, "%d %f %s", &(tmp.n), &(tmp.score), tmp.arr);
printf("%d %f %s\n", tmp.n, tmp.score, tmp.arr);
return 0;
}
3.12 fwrite():写入二进制
cpp
//fwrite:将数据以二进制的形式写入
int main()
{
struct S s = { "张三", 20, 55.6 };
FILE* pf = fopen("test.txt", "rb");
if (pf == NULL)
{
return 0;
}
//二进制的形式都文件
//表示将s中的数据写入到pf中
fwrite(&s, sizeof(struct S), 1, pf);
printf("%s %d %lf\n", &s.name, &s.age, &s.score);
fclose(pf);
pf = NULL;
return 0;
}
3.13 fread:通过二进制的方式读出数据
cpp
//fread:通过二进制的方式输出数据
int main()
{
struct S tmp = { 0 };
FILE* pf = fopen("test.txt", "rb");
if (pf == NULL)
{
return 0;
}
//二进制的形式都文件
//表示将pf中的数据写到tmp中
fread(&tmp, sizeof(struct S), 1, pf);
printf("%s %d %lf\n", tmp.name, tmp.age, tmp.score);
fclose(pf);
pf = NULL;
return 0;
}
4. 文件的随机读写
4 .1 fseek
根据文件指针的位置和偏移量来定位文件指针
cpp
int fseek ( FILE * stream, long int offset, int origin );
cpp
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
return 0;
}
//1.定位文件指针
//fseek(要读取的文件,移动的字节数,起始位置)
//SEEK_END:表示向前移动2个位置
fseek(pf, -2, SEEK_END);
//2.读取文件
int ch = fgetc(pf);
printf("%c\n", ch);
fclose(pf);
pf = NULL;
return 0;
}
4.2 ftell
返回文件指针相对于起始位置的偏移量
cs
long int ftell ( FILE * stream );
cpp
//ftell:返回文件指针相对于起始位置的偏移量
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
return 0;
}
//2.读取文件
int post = ftell(pf);
printf("%d\n", post);//打开默认地址为0
fclose(pf);
pf = NULL;
return 0;
}
cpp
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
return 0;
}
//此时读取一个字符,则表示跳过第一个字符
fgetc(pf);
//2.读取文件
int post = ftell(pf);
printf("%d\n", post);//故此时输出为1
fclose(pf);
pf = NULL;
return 0;
}
4.3 rewind
让文件指针的位置回到文件的起始位置
cpp
void rewind ( FILE * stream );
cpp
//rewind:回到文件的起始位置
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
return 0;
}
int ch = fgetc(pf);
printf("%c\n", ch);//读取出a
rewind(pf);//返回原来的位置
ch = fgetc(pf);
printf("%c\n", ch);//读取出a
fclose(pf);
pf = NULL;
return 0;
}
5. 文本文件和二进制文件
6. 文件读取结束的判定
6.1 被错误使用的feof
feof:是用于判断文件读取结束的原因
cpp
/*
文本文件读取结束:fgetc:pf==EOF
fgets:pf=null
二进制文件读取结束:fread判断返回值是否小于实际要读的个数
*/
int main()
{
//EOF
//feof();//EOF - end of file - 文件结束标志
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
return 0;
int ch = fgetc(pf);
printf("%d\n", ch);//-1
fclose(pf);
pf = NULL;
return 0;
}
文本文件例子
二进制文件例子:
6.2 perror和strerror
cpp
/*
strerror:把错误码对应的错误信息的字符串地址返回
peerror(s):参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。
*/
int main()
{
//strerror - 把错误码对应的错误信息的字符串地址返回
//printf("%s\n", strerror(errno));
//perror
FILE* pf = fopen("test2.txt", "r");
if (pf == NULL)
{
perror("open file test2.txt");//open file test2.txt:No such file or directory
return 0;
}
//读文件
fclose(pf);
pf = NULL;
return 0;
}
C语言 | perror函数使用详解_c语言线程perror_嵌入式大杂烩的博客-CSDN博客
7.练习
1. 求内存大小
cpp
struct {
int a;//0-3 4字节
char b;//4 1字节
short c;//6-7 2字节【因为是2字节,所以要使用2的倍数】
short d;//8-9 2字节【因为是2字节,所以要使用2的倍数】
};//10字节
//所以我们应该直接+到12字节【地址为12-->(1,2,4)的最大倍数】
2.sizeof(struct)
cpp
struct A
{
int a;//0-3
short b;//4-5
//6-7
int c;//8-11
char d;//12
//13 14 15
};//13 -- > 16【(1,2,4)的最大倍数】
struct B
{
int a;//0-3
short b;//4-5
char c;//6
//7
int d;//8-11
};//12【(1,2,4)的最大倍数】
int main()
{
printf("%d\n", sizeof(struct A));//16
printf("%d\n", sizeof(struct B));//12
return 0;
}
3.
cpp
#pragma pack(4)/*编译选项,表示4字节对齐 平台:VS2013。语言:C语言*/
int main(int argc, char* argv[])
{
struct tagTest1
{
short a;//0-1
char d;//2
3
long b;//4-7
long c;//8-11
};//12
struct tagTest2
{
long b;//0-3
short c;//4-5
char d;//6
7
long a;//8-11
};//12
struct tagTest3
{
short c;//0-1
2 3
long b;//4-7
char d;//8
9-11
long a;//12-15
};//16
struct tagTest1 stT1;
struct tagTest2 stT2;
struct tagTest3 stT3;
printf("%d %d %d", sizeof(stT1), sizeof(stT2), sizeof(stT3));
return 0;
}
#pragma pack()