程序中的数据都是放在内存中的,
如果要保存这些数据不至于丢失,可以使用文件来保存信息,文件是放在硬盘上
文件的打开 关闭
文件打开时,内存创建文件信息区,文件信息区是用来存放文件的相关信息。
这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE。文件信息区中是FILE类型的结构体
通过一个FILE的指针来维护这个FILE结构的变量
FILE* pf;
fopen函数和fclose函数
//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );
当使用fopen函数打开文件时,mode可以有多种可能的值
文件的打开和关闭(只写)
cs
int main()
{
//打开文件
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
perror("fopen");
return;
}
//操作文件
//关闭文件
int rst = fclose(pf);
pf = NULL;//文件关闭后要对指针赋空
return 0;
}
文件的打开和关闭(只读)
cs
int main()
{
//打开文件
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return;
}
//操作文件
//关闭文件
int rst = fclose(pf);
pf = NULL;//文件关闭后要对指针赋空
return 0;
}
添加文件后
相对路径
.表示当前路径(一般默认的就是当前路径)
..表示上一级路径
cs
int main()
{
//打开文件
FILE* pf = fopen("..\\data.txt", "r");//打开上一级路径的data.txt文件
if (pf == NULL)
{
perror("fopen");
return;
}
//操作文件
//关闭文件
int rst = fclose(pf);
pf = NULL;//文件关闭后要对指针赋空
return 0;
}
添加文件后
当前路径下的
绝对路径
文件的顺序读写
在这之中的所有输出流就包括1.文件输出流 2.标准输出流stdout
fputc函数
int fputc ( int character, FILE * stream );
向文件写入字符
写入成功,返回所写的字符的ASCII值。如果发生写入错误,则返回EOF并设置错误指示器
cs
int main()
{
//打开文件,只写
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
perror("foprn");
return;
}
//操作文件,向文件写入
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);
fputc('d', pf);
//关闭文件
int rst = fclose(pf);
pf = NULL;
return 0;
}
写入之前
写入之后
fputc也可以向屏幕写入
cs
int main()
{
//直接向屏幕上写入
fputc('a', stdout);//stdout是标准输出流
fputc('b', stdout);
fputc('c', stdout);
fputc('d', stdout);
return 0;
}
fgetc函数
int fputc(int character, FILE* stream);
函数的功能:
将字符写入流并推进位置指示器。字符被写在流的内部位置指示器指示的位置,然后自动前进一个
函数的返回值:
成功后,返回读取的字符(提升为int值)。
返回类型为int,以容纳表示失败的特殊值EOF:如果位置指示符位于文件末尾,则函数返回EOF并设置流的EOF指示符(feof)。
如果发生其他读取错误,该函数也会返回EOF,但会设置其错误指示器(EOF的本质是-1)
演示fgetc函数
cs
int main()
{
//打开文件,只读
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("foprn");
return;
}
//操作文件,从文件读出
int ch = 0;
ch = fgetc(pf);
if (ch != EOF)//读取成功
{
printf("%c ", ch);
}
ch = fgetc(pf);
if (ch != EOF)//读取成功
{
printf("%c ", ch);
}
ch = fgetc(pf);
if (ch != EOF)//读取成功
{
printf("%c ", ch);
}
ch = fgetc(pf);
if (ch != EOF)//读取成功
{
printf("%c ", ch);
}
//关闭文件
int rst = fclose(pf);
pf = NULL;
return 0;
}
正确的代码应该这样写
cs
int main()
{
//打开文件,只读
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("foprn");
return;
}
//操作文件
int ch = 0;
while ((ch = fgetc(pf)) != EOF)//只有当读文件的指示符到文件末尾,或者读取文件错误会返回EOF
{
printf("%c ", ch);
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
fputs函数
int fputs ( const char* str,FILE * stream );
将str指向的C字符串写入文件中。函数从指定的地址(str)开始复制,直到到达终止的空字符("\0")。此终止空字符不会复制到流中。
请注意,fputs与put的不同之处不仅在于可以指定目标流,而且fputs不会写入额外的字符,而put会自动在末尾添加换行符。
返回值
成功后,返回一个非负值。出现错误时,该函数返回EOF并设置错误指示器(ferror)。
cs
int main()
{
//打开文件 只写
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//操作文件
int rst1 = fputs("hello ", pf);
int rst2 = fputs("world", pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
写入前
写入后
fputs在放入一行字符串时遇到\0就停止
cs
int main()
{
//打开文件 只写
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//操作文件
int rst1 = fputs("hello\n", pf);
int rst2 = fputs("worldxxxxx\0xxxxxx", pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
fputs打印信息到屏幕上
cs
int main()
{
int rst1 = fputs("hello ", stdout);
int rst2 = fputs("world", stdout);//这样就把信息打印在屏幕上
return 0;
}
fgets函数
char* fgets(char* str, int num, FILE* stream);
函数的功能:
从文件中读取字符,并将其作为C字符串存储到str中,直到读取了(num-1)个字符,或者到达换行符或文件末尾,以先发生者为准。
换行符会使fgets停止读取,但函数会将其视为有效字符,并将其包含在复制到str的字符串中。
在复制到str的字符之后,会自动附加一个终止空字符。
请注意,fgets与gets有很大不同:fgets不仅接受流参数,还允许指定str的最大大小,并在字符串中包含任何结束的换行符。
函数的返回值:
成功后,函数返回str。
如果在尝试读取字符时遇到文件末尾,则设置eof指示符(feof)。如果在读取任何字符之前发生这种情况,则返回的指针为空指针(str的内容保持不变)。
如果发生读取错误,则设置错误指示符(ferror)并返回空指针(但str指向的内容可能已更改)。
读到指定位置截止
cs
int main()
{
//打开文件 只写
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//操作文件
int rst1 = fputs("hello\nbbbb", pf);
int rst2 = fputs("worldxxxxx\0xxxxxx", pf);
//关闭文件
fclose(pf);
pf = NULL;
//打开文件 只读
FILE* pf1 = fopen("data.txt", "r");
if (pf1 == NULL)
{
perror("fopen");
return 1;
}
//操作文件
char arr[100] = { 0 };//这个arr表示把文件读到arr这个数组中
//fgets(arr, 3, pf1);//读入3-1个也就是前2个
//printf("%s", arr);
fgets(arr, 4, pf1);
printf("%s", arr);
//fgets(arr, 100, pf1);
//printf("%s", arr);
fclose(pf1);
pf1 = NULL;
return 0;
}
一行有\n那么就读到\n结束
cs
int main()
{
//打开文件 只写
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//操作文件
int rst1 = fputs("hello\nbbbb", pf);
int rst2 = fputs("worldxxxxx\0xxxxxx", pf);
//关闭文件
fclose(pf);
pf = NULL;
//打开文件 只读
FILE* pf1 = fopen("data.txt", "r");
if (pf1 == NULL)
{
perror("fopen");
return 1;
}
//操作文件
char arr[100] = { 0 };//这个arr表示把文件读到arr这个数组中
//fgets(arr, 3, pf1);//读入3-1个也就是前2个
//printf("%s", arr);
fgets(arr, 8, pf1);
printf("%s", arr);
//fgets(arr, 100, pf1);
//printf("%s", arr);
fclose(pf1);
pf1 = NULL;
return 0;
}
无\n读到\0 也截止
**fprintf函数,**fscanf函数
和printf 函数类似,只是多了个参数,FILE* pf
和scanf 函数类似,只是多了个参数,FILE* pf
向文件写入(fprintf)
cs
struct Stu
{
char name[10];
int age;
};
int main()
{
struct Stu s1 = { "zhangsan",16 };
//打开文件 只写
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//操作文件,向文件写入
fprintf(pf, "%s\n%d", s1.name, s1.age);
fclose(pf);
pf = NULL;
return 0;
}
从文件读出(fscanf)
cs
struct Stu
{
char name[10];
int age;
};
int main()
{
struct Stu s = { 0 };
//打开文件 只读
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//从文件读出
fscanf(pf, "%s%d", s.name, &(s.age));
//从文件读出的数据存储在结构体变量s中
printf("%s-%d", s.name, s.age);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
fwrite函数(二进制写入)
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
函数的功能:
从ptr指向的内存块向流中的当前位置写入一个计数元素数组,每个元素的大小为(size)。
文件的位置指示器按写入的总字节数前进。
在内部,该函数将ptr指向的块解释为一个无符号char类型的(size*count)元素数组,并将它们顺序写入流,就像每个字节都调用了fputc一样。
函数的返回值:
返回成功写入的元素总数。
如果此数字与计数参数不同,则写入错误会阻止函数完成。在这种情况下,将为流设置错误指示器(ferror)。
如果size或计数为零,则函数返回零,错误指示器保持不变。size_t是无符号整数类型。
cs
int main()
{
//打开文件
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
FILE* pf = fopen("data.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//二进制写入
fwrite(arr, sizeof(arr[0]), 10, pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
fread函数(二进制读出 )
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
从流中读取一个计数元素数组,每个元素的大小为(size),并将其存储在ptr指定的内存块中。
流的位置指示器按读取的总字节数前进。
如果成功,读取的字节总数为(size*count)。
返回值
返回成功读取的元素总数。
如果此数字与计数参数不同,则可能是发生了读取错误,或者在读取时到达了文件末尾。
在这两种情况下,都设置了适当的指示器,可以分别用ferror和feof进行检查。
如果size或count为零,则函数返回零,流状态和ptr指向的内容都保持不变。size_t是一个无符号整数类型。
cs
//二进制的方式从文件读出
//size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
int main()
{
int arr[10] = { 0 };
//打开文件
FILE* pf = fopen("data.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读出数据
fread(arr, sizeof(arr[0]), 10, pf);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
sscanf和sprintf函数
int sscanf ( const char * s, const char * format, ...);
int sprintf(char* str, const char* format, ...);
cs
struct S
{
float f;
char ch;
int a;
};
int main()
{
struct S s = { 3.14f,'w',100 };
printf("%f-%c-%d", s.f, s.ch, s.a);
printf("\n");
char arr[100] = { 0 };
//把格式化数据转化成字符串
sprintf(arr, "%f-%c-%d", s.f, s.ch, s.a);
printf("%s", arr);
//把字符串转化成格式化数据
struct S s1 = { 0 };
sscanf(arr, "%f-%c-%d", &(s1.f), &(s1.ch), &(s1.a));
printf("\n");
printf("%f\n", s1.f);
printf("%c\n", s1.ch);
printf("%d\n", s1.a);
return 0;
}
文件的随机读写
fseek,ftell,rewind函数
int fseek ( FILE * stream, long int offset, int origin )
第一个参数是FILE*类型的地址,第二个参数表示的是想要改变的偏移量
第三个参数有三种形式
SEEK_SET 表示文件指针的起始位置(这里的文件指针指的是文件里面的光标)
SEEK_CUR 表示文件指针的当前位置
SEEK_END 表示文件指针的末尾
long int ftell ( FILE * stream );
ftell函数可以告诉我们当前文件指针相较于起始位置的偏移量
rewind函数让文件指针回到起始位置
void rewind ( FILE * stream );
cs
int main()
{
//打开文件
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
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
ch = fgetc(pf);
printf("%c\n", ch);//d
long int num = ftell(pf);
printf("偏移量为%d\n", num);
//如果按照顺序接下来要读e
//但是可以通过fseek函数改变顺序
fseek(pf,-3 , SEEK_CUR);//b
//fseek(pf, 0, SEEK_SET);
//fseek(pf, -9, SEEK_END);
ch = fgetc(pf);
printf("%c\n", ch);
rewind(pf);//让指针回到起始位置
ch = fgetc(pf);
printf("%c\n", ch);
return 0;
}
文本文件和二进制文件
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。(看不懂)
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件(可以看懂)
如有整数10000,把10000的每一位当作字符,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节)
而二进制形式输出,则在磁盘上只占4个字节。
文件读取结束的判定
fgetc 成功返回字符的ASCII值,失败返回EOF
fgets 成功,返回存放数据的起始位置,失败返回空指针
fread 读到几个元素返回值就是几,如果返回值小于实际要读的个数,则可能是发生了读取错误,或者在读取时到达了文件末尾。
模拟实现拷贝
cs
//实现文件的拷贝
int main()
{
//打开文件 只读
FILE* pf1 = fopen("data1.txt", "r");
if (pf1 == NULL)
{
perror("fopen");
return 1;
}
//打开文件 只写
FILE* pf2 = fopen("data2.txt", "w");
if (pf2 == NULL)
{
perror("fopen");
//文件二打开失败就会退出程序,那么在这之前应该关闭文件一
fclose(pf1);
pf1 = NULL;
return 1;
}
//两个文件都打开成功,拷贝文件
int ch = 0;
while ((ch=fgetc(pf1)) != EOF)//如果不为EOF就说明读取成功,拷贝
{
fputc(ch, pf2);
}
//关闭文件
fclose(pf1);
pf1 = NULL;
fclose(pf2);
pf2 = NULL;
return 0;
}
文件缓冲器
cs
#include <stdio.h>
#include <windows.h>
int main()
{
FILE* pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
//注:fflush 在高版本的VS上不能使用了
printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在关闭文件的时候,也会刷新缓冲区
pf = NULL;
return 0;
}
因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件