前言
为什么使用文件什么是文件
文件的打开和关闭文件的顺序读写文件的随机读写
文本文件和二进制文件文件读取结束的判定文件缓冲区
一、文件指针
1.文件打开和关闭
//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );
2.打开方式
3.实现代码模板
/* fopen fclose example */
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{
FILE *pf= fopen ("myfile.txt","w");
//文件操作
if (pf==NULL)
{
//文件打开失败
printf("%s\n",strerror(errno));
return 0;
}
//写文件
//关闭文件
fclose(pf);
pf= NULL;
}
二、文件顺序读写
1.fputc(从文件里面读一个字符)
char fputc( char c, FILE *stream );
c:读取的字符
stream:对应的只指针
/* fopen fclose example */
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{
FILE *pf= fopen ("myfile.txt","w");
//文件操作
if (pf==NULL)
{
//文件打开失败
printf("%s\n",strerror(errno));
return 0;
}
//写文件
//fput('a',pf);
//fput('b',pf);
//fput('c',pf);
char ch = 0;
for(ch = 'a'; ch <= 'z'; ch++){
fputc(ch, pf);
}
//关闭文件
fclose(pf);
pf= NULL;
}
结果:
2. 三种流
标准输入流:stdin
标准输出流:stdout
标准错误流 :stderr
从键盘上输入内存时,就是将数据输入到标准输入流
从内存上输入显示器上时,就是将数据输入到标准输出流
使用:将指针替换为所需要的三种流即可。
3.fgetc(把一个字符写到文件中)
int fgetc( FILE*stream );
stream:对应的只指针
/* fopen fclose example */
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("myfile.txt", "w");
//文件操作
if (pf == NULL)
{
//文件打开失败
printf("%s\n", strerror(errno));
return 0;
}
//读文件
int ch = fgetc(pf);
printf("%c\n",ch);
int ch = fgetc(pf);
printf("%c\n", ch);
int ch = fgetc(pf);
printf("%c\n", ch);
int ch = fgetc(pf);
printf("%c\n", ch);
//关闭文件
fclose(pf);
pf = NULL;
}
4.fputs (整行输出)
int fputs(const char *s, FILE *stream);
把参数s指向的字符串写入stream指向的流,但不包括字符串末尾的空字符
/* fopen fclose example */
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("myfile.txt", "w");
//文件操作
if (pf == NULL)
{
//文件打开失败
printf("%s\n", strerror(errno));
return 0;
}
//读文件
fputs("hello world",pf);
//关闭文件
fclose(pf);
pf = NULL;
}
4.fgets (整行输入)
char * fgets(char * s, int n,FILE *stream);
从stream指向的输入流中读取 unsigned char 型的字符串
/* fopen fclose example */
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("myfile.txt", "w");
//文件操作
if (pf == NULL)
{
//文件打开失败
printf("%s\n", strerror(errno));
return 0;
}
char buf[1000] = { 0 };
//读文件
fgets(buf, 1000, pf);
printf("%s", buf);
//关闭文件
fclose(pf);
pf = NULL;
}
5..拷贝文件
#include<stdio.h>
#include<errno.h>
int main()//拷贝文件
{
FILE* pr = fopen("text.txt", "r");
if (pr == NULL)
{
printf("open for read:%s\n", strerror(errno));
return 0;
}
FILE* pw = fopen("text2.txt","w");
if (pr == NULL)
{
printf("open for write:%s\n", strerror(errno));
close(pr);
pr = NULL;
return 0;
}
int ch=0;
//拷贝文件
while ((ch=fgetc(pr)) != EOF)
{
fputc(ch, pw);
}
//关闭文件
fclose(pr);
pr == NULL;
fclose(pw);
pw == NULL;
return 0;
}
6. fwrite(将一块内存区域中的数据写入到本地文本)二进制
size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
-- buffer:指向数据块的指针
-- size:每个数据的大小,单位为Byte(例如:sizeof(int)就是4)
-- count:数据个数
-- stream:文件指针
int main()
{
struct stu s[2] - { {"张三",20,95.5}, { "lisi",16,66.5 } };
FILE* pf = fopen("myfile.txt", "wb");
//文件操作
if (pf == NULL)
{
//文件打开失败
printf("%s\n", strerror(errno));
return 0;
}
//二进制打开
fwrite(&s, sizeof(struct stu), 2, pf);
//关闭文件
fclose(pf);
pf = NULL;
}
7.fread(从一个文件流中读取数据)二进制
size_t fread(void *buffer, size_t size, size_t count, FILE *stream);
-- buffer:指向数据块的指针
-- size:每个数据的大小,单位为Byte(例如:sizeof(int)就是4)
-- count:数据个数
-- stream:文件指针
int main()
{
struct stu s[2] = { 0 };
FILE* pf = fopen("myfile.txt", "rb");
//文件操作
if (pf == NULL)
{
//文件打开失败
printf("%s\n", strerror(errno));
return 0;
}
//二进制读文件
fread(s, sizeof(struct stu), 2, pf);
printf("%s %d %lf\n",s[0].name, s[0].age, s[0].d);
printf("%s %d %lf\n",s[1].name, s[1].age, s[1].d);
//关闭文件
fclose(pf);
pf = NULL;
}
8.对比以下函数
scanf从标准输入流(stdin)上进行格式化输入的函数
printf向标出输出流( stdout)上进行格式化的输出函数
fscanf可以从标准输入流(stdin)/指定的文件流上读取格式化的数据
fprintf把数据按照格式化的方式输出到标准输出流(stdout)/指定的文件流
fscanf()函数(有点像正则表达式):
功 能: 从一个流中执行格式化输入,fscanf遇到空格和换行时结束,注意空格时也结束。
用 法:int fscanf(FILE *stream, char *format,[argument...]);
int fscanf(文件指针,格式字符串,输入列表);
fscanf(fp,"%s%d%lf",a,&b,&c)
返回值:整型,数值等于[argument...]的个数
fprintf(pf, "%s %d %s", s.name, s.age, s.sex);//把数据写入文件
sscanf可以从一个字符串中提取(转化)出格式化数据
sprintf把一个格式化的数据转换成字符串
int sscanf( const char *buffer, const char *format [, argument ] ... );
int sprintf( char *buffer, const char *format [, argument] ... );
三、文件随机读写
1.fseek
根据文件指针的位置和偏移量来定位文件指针.
int fseek ( FILE * stream, long int offset, int origin );
描 述: 函数设置文件指针stream的位置。如果执行成功,stream将指向以fromwhere为基准,偏移offset个字节的位置。如果执行失败(比如offset超过文件自身大小),则不改变stream指向的位置。
注意:
第一个参数stream为文件指针
第二个参数offset为偏移量,整数表示正向偏移,负数表示负向偏移
第三个参数origin设定从文件的哪里开始偏移,可能取值为:SEEK_CUR、 SEEK_END 或 SEEK_SET
SEEK_SET: 文件开头
SEEK_CUR: 当前位置
SEEK_END: 文件结尾
其中SEEK_SET,SEEK_CUR和SEEK_END和依次为0,1和2.简言之:
fseek(fp,100L,0);把fp指针移动到离文件开头100字节处;
fseek(fp,100L,1);把fp指针移动到离文件当前位置100字节处;
ffseek(fp,-100L,2);把fp指针退回到离文件结尾100字节处。
/* ftell example : getting size of a file */
#include <stdio.h>
int main ()
{
FILE * pFile;
long size;
pFile = fopen ("myfile.txt","rb");
if (pFile==NULL) perror ("Error opening file");
else
{
fseek (pFile, 0, SEEK_END); // non-portable
size=ftell (pFile);
fclose (pFile);
printf ("Size of myfile.txt: %ld bytes.\n",size);
}
return 0;
}
2.ftell
long ftell(FILE *fp);
返回文件指针相对于起始位置的偏移量
/* ftell example : getting size of a file */
#include <stdio.h>
int main ()
{
FILE * pFile;
long size;
pFile = fopen ("myfile.txt","rb");
if (pFile==NULL) perror ("Error opening file");
else
{
fseek (pFile, 0, SEEK_END); // non-portable
size=ftell (pFile);
fclose (pFile);
printf ("Size of myfile.txt: %ld bytes.\n",size);
}
return 0;
}
3 rewind
void rewind ( FILE * stream );
让文件指针的位置回到文件的起始位置
/* rewind example */
#include <stdio.h>
int main ()
{
int n;
FILE * pFile;
char buffer [27];
pFile = fopen ("myfile.txt","w+");
for ( n='A' ; n<='Z' ; n++)
fputc ( n, pFile);
rewind (pFile);
fread (buffer,1,26,pFile);
fclose (pFile);
buffer[26]='\0';
puts (buffer);
return 0;
}
四、文本文件和二进制文件
二进制文件以二进制形式存储,文本文件以ASCII存储的。
字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
#include <stdio.h>
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
fclose(pf);
pf = NULL;
return 0;
}
//0000 0000 0000 0000 0010 0111 0001 0000
// 0 0 0 0 2 7 1 0
二进制结果:
五、文件读取结束判定
牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。
而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束
1.文本文件读取是否结束,判断返回值是否为EOF ( fgetc),或者NULL) ( fgets)例如:
fgetc判断是否为EOF。
fgets判断返回值是否为NULL。
2.二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。例如:
fread判断返回值是否小于实际要读的个数。
文本文件:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int c; // 注意:int,非char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
//是否成功打开文件
if(!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
{
putchar(c);
}
//判断是什么原因结束的
//IO错误
if (ferror(fp))
puts("I/O error when reading");
//判断是否是读到文件末尾
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
二进制文件:
#include <stdio.h>
enum { SIZE = 5 };
int main(void)
{
double a[SIZE] = {1.,2.,3.,4.,5.};
FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式
fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组
fclose(fp);
double b[SIZE];
fp = fopen("test.bin","rb");
size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组
if(ret_code == SIZE) {
puts("Array read successfully, contents: ");
for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
putchar('\n');
} else { // error handling
//判断是否读到文件错误
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\n");
//判断是否读到文件末尾
else if (ferror(fp)) {
perror("Error reading test.bin");
}
}
fclose(fp);
}
六、文件缓冲区
从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。
#include <stdio.h>
#include <windows.h>
//VS2013 WIN10环境测试
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语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件,如果不做,可能导致读写文件的问题。
总结
可以更新通讯录文件操作部分。