一 文件
1 程序文件
包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。
2 数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
3 文件名
文件名包含3部分:文件路径+文件名主干+文件后缀
c:\code\test.txt
二 文件打开和关闭
1 文件指针
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE.
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* pf;//文件指针变量
2 打开和关闭
fopen 与 fclose
FILE *fopen( const char *filename, const char *mode );
Return Value
Each of these functions returns a pointer to the open file. A null pointer value indicates an error.
"r"
Opens for reading. If the file does not exist or cannot be found, the fopen call fails.
"w"
Opens an empty file for writing. If the given file exists, its contents are destroyed.
"a"
Opens for writing at the end of the file (appending) without removing the EOF marker before writing new data to the file; creates the file first if it doesn’t exist.
"r+"
Opens for both reading and writing. (The file must exist.)
"w+"
Opens an empty file for both reading and writing. If the given file exists, its contents are destroyed.
?
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
//打开文件
FILE* pf = fopen("test.txt","w");//注意是""
if (pf == nullptr)
{
printf("fopen fail\n");
exit(-1);
}
//写文件
//...
//关闭文件
fclose(pf);
pf = nullptr;
return 0;
}
//打开文件
FILE* pf = fopen("tesst.txt","r");//注意是""
if (pf == nullptr)
{
//printf("fopen fail\n");
printf("%s\n",strerror(errno));//No such file or directory
exit(-1);
}
解释:
“r”:以只读的形式打开文本文件(不存在则出错) “w”:以只写的形式打开文本文件(若不存在则新建,反之,则从文件起始位置写,覆盖原内容) “a”:以追加的形式打开文本文件(若不存在,则新建;反之,在原文件后追加) “r+”:以读写的形式打开文本文件(读时,从头开始;写时,新数据只覆盖所占的空间) “wb”:以只写的形式打开二进制文件 “rb”:以只读的形式打开二进制文件 “ab”:以追加的形式打开一个二进制文件 “rb+”:以读写的形式打开二进制文件。 “w+”:首先建立一个新文件,进行写操作,然后从头开始读(若文件存在,原内容将全部消失) “a+”:功能与”a”相同。只是在文件尾部追加数据后,可以从头开始读 “wb+”:功能与”w+”相同。只是在读写时,可以由位置函数设置读和写的起始位置 “ab+”:功能与”a+”相同。只是在文件尾部追加数据之后,可以由位置函数设置开始读的起始位置
3 相关的库函数
文件的打开 fopen():打开文件
文件的关闭 fclose():关闭文件
文件的读写 fgetc():读取一个字符 适用于所有输入流 fputc():写入一个字符 fgets():读取一个字符串 fputs():写入一个字符串 fprintf():写入格式化数据 fscanf():格式化读取数据 fread():读取数据 fwrite():写入数据
文件状态检查 feof():文件是否结束 ferror():文件读/写是否出错 clearerr():清除文件错误标志 ftell():文件指针的当前位置
文件指针定位 rewind():把文件指针移到开始处 fseek():重定位文件指针
三 文件顺序读写
1 fgetc&fputc
int main(int argc, char const *argv[])
{
//打开文件
FILE* pf = fopen("data.txt","w");//注意是""
if (pf == nullptr)
{
printf("%s\n",strerror(errno));//No such file or directory
exit(-1);
}
//写文件
char ch = 0;
for (ch = 'a'; ch < 'z'; ch++)
{
fputc(ch,stdout);//写到屏幕上了 abcdefghijklmnopqrstuvwxy
}
//关闭文件
fclose(pf);
pf = nullptr;
return 0;
}
for (ch = 'a'; ch < 'z'; ch++)
{
fputc(ch,pf);//写到文件里面 abcdefghijklmnopqrstuvwxy
}
//读文件
int ch = 0;//用int既能接受ASCII 也能接受EOF
while ((ch = fgetc(pf)) != EOF)
{
printf("%c ",ch);
}
2 fputs&fgets
//写一行
fputs("hello world\n",pf);
fputs("hello\n",pf);
//读文件 读一行
char buf[1000] = {0};
fgets(buf, 1000, pf); //实际读999个
printf("%s",buf);
3 文件拷贝
//将data.txt拷贝一份 生成data2.txt
int main(int argc, char const *argv[])
{
FILE *pr = fopen("data.txt", "r");
if (pr == NULL)
{
printf("open for reading::%s", strerror(errno));
return 0;
}
FILE *pw = fopen("data2.txt", "w");
if (pw == NULL)
{
printf("open for writing::%s", strerror(errno));
// pw开辟失败
fclose(pr);
pr = NULL;
return 0;
}
//拷贝文件
int ch = 0;
while ((ch = fgetc(pr)) != EOF)
{
fputc(ch, pw); //读一个写一个
}
//关闭文件
fclose(pr);
pr = NULL;
fclose(pw);
pw = NULL;
system("pause");
return 0;
}
4 fprintf&fscanf
struct Stu
{
char name[20];
int age;
double d;
};
int main()
{
struct Stu s = { "张三", 20, 95.5 };
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 0;
}
//写格式化的数据
fprintf(pf, "%s %d %lf", s.name, s.age, s.d);
fclose(pf);
pf = NULL;
return 0;
}
fprintf(stdout, "%s %d %lf", s.name, s.age, s.d);
//输出到屏幕上也行
int main()
{
struct Stu s = { 0 };
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 0;
}
//读格式化的数据 从data.txt中读
fscanf(pf, "%s %d %lf", s.name, &(s.age), &(s.d));
printf("%s %d %lf\n", s.name, s.age, s.d);
fclose(pf);
pf = NULL;
return 0;
}
5 fwrite&fread
//二进制的写
int main()
{
struct Stu s[2] = {{"张三", 20, 96}, {"李四", 20, 86}};
FILE *pf = fopen("data.txt", "wb");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 0;
}
//按照二进制的方式写入
//数组名本身就是地址
fwrite(s,sizeof(struct Stu),2,pf);//写2组
//注意二进制的方式写进去后,文本方式打开的是乱码,需要通过二进制编辑器打开
fclose(pf);
pf = NULL;
return 0;
}
//二进制的读
int main()
{
struct Stu s[2] = {0};
FILE *pf = fopen("data.txt", "rb");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 0;
}
//按照二进制的方式读出来
fread(s,sizeof(struct Stu),2,pf);//写2组
printf("%s %d %lf\n",s[0].name,s[0].age,s[0].d);
fclose(pf);
pf = NULL;
return 0;
}
6 文件版Contact
void LoadContact(Contact *pc)
{
//从文件加载数据
FILE *pf = fopen("contact.txt", "rb");
if (pf == nullptr)
{
printf("InitContact:: open for reading :: %s\n", strerror(errno));
}
//读取文件
Peoinfo buf = {0};
while (fread(&buf, sizeof(Peoinfo), 1, pf))
{
//放进去前要检测容量是否足够
check_capacity(pc);
pc->data[pc->sz] = buf;
pc->sz++;
}
}
void SaveContact(Contact *pc)
{
//打开文件
FILE *pf = fopen("contact.txt", "wb");
if (pf == nullptr)
{
printf("SaveContact::%s\n", strerror(errno));
exit(-1);
}
//写文件
int i = 0;
for (i = 0; i < pc->sz; i++)
{
//一次写1组,data指向一块连续空间,每次写入的地址要增加
fwrite(pc->data + i, sizeof(Peoinfo), 1, pf);
}
//关闭文件
fclose(pf);
pf = nullptr;
}
7 sscanf&sprintf
scanf 从标准输入流(stdin)上进行格式化输入 printf 想标准输出流(stdout)上进行格式化的输出
fscanf 从标准输入流/指定的文件流上读取格式化的数据 fprintf 把数据按照格式化的方式输出到标准输出流/指定的文件流
sscanf 从一个字符串中提取(转化)出格式化的数据 sprintf 吧一个格式化的数据转换成字符串
int main(int argc, char const *argv[])
{
struct Stu s = {"Tom", 20, 98};
struct Stu tmp = {0};
char buf[100] = {0};
sprintf(buf,"%s %d %lf",s.name,s.age,s.d);
printf("%s\n",buf);//Tom 20 98.000000
sscanf(buf,"%s %d %lf",s.name,&(s.age),&(s.d));
printf(buf,"%s %d %lf",tmp.name,tmp.age,tmp.d);//Tom 20 98.000000
return 0;
}
四 文件的随机读写
1 fssek
根据文件指针的位置和偏移量来定位文件指针
int fseek ( FILE * stream, long int offset, int origin );
int main(int argc, char const *argv[])
{
FILE *pf = fopen("test.txt", "r");//abcdef
if (pf == nullptr)
{
cout << strerror(errno) << endl;
return 0;
}
//读文件
char ch = fgetc(pf);
cout << ch << endl;
ch = fgetc(pf);
cout << ch << endl;
//定位文件指针
//fseek(pf, 3, SEEK_CUR);
//fseek(pf,5,SEEK_SET);//SET表示从起始位置开始偏移
fseek(pf, -1, SEEK_END);//END从结束位置开始偏移,偏移-1就指向end
ch = fgetc(pf);
cout << ch << endl;//f
fclose(pf);
pf = nullptr;
system("pause");
return 0;
}
int main(int argc, char const *argv[])
{
FILE *pf = fopen("test.txt", "w");//abcdef
if (pf == nullptr)
{
cout << strerror(errno) << endl;
return 0;
}
//写文件
int ch = 0;
for (ch = 'a'; ch <= 'z'; ch++)
{
fputc(ch,pf);
}
//读文件
fseek(pf, -1, SEEK_END);//END从结束位置开始偏移,偏移-1就指向end
fputc('@',pf);//把z改成#
fclose(pf);
pf = nullptr;
system("pause");
return 0;
}
2 ftell
返回文件指针相对于起始位置的偏移量
long int ftell ( FILE * stream );
int main(int argc, char const *argv[])
{
FILE *pf = fopen("test.txt", "r");//abcdef
if (pf == nullptr)
{
cout << strerror(errno) << endl;
return 0;
}
//读文件
char ch = fgetc(pf);
cout << ch << endl;//a
ch = fgetc(pf);
cout << ch << endl;//b
//返回偏移量
cout << ftell(pf) << endl;//2
fclose(pf);
pf = nullptr;
system("pause");
return 0;
}
3 rewind
让文件指针的位置回到文件的起始位置
void rewind ( FILE * stream );
int main(int argc, char const *argv[])
{
FILE *pf = fopen("test.txt", "r");//abcdef
if (pf == nullptr)
{
cout << strerror(errno) << endl;
return 0;
}
//读文件
char ch = fgetc(pf);
cout << ch << endl;//a
ch = fgetc(pf);
cout << ch << endl;//b
//返回偏移量
cout << ftell(pf) << endl;//2
rewind(pf);
//fseek(pf, 0, SEEK_SET); 也有这样的效果
cout << ftell(pf) << endl;//0
fclose(pf);
pf = nullptr;
system("pause");
return 0;
}
五 文件结束判定
1 feof
feof不是用来判断文件结束的,而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇
到文件尾结束
2 文本文件
fgetc判断是否为EOF
fgets判断返回值是否为NULL
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);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
3 二进制文件
fread判断返回值是否小于实际要读的个数
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
#include <stdio.h>
enum { SIZE = 5 };
int main(void)
{
double a[SIZE] = {1.0,2.0,3.0,4.0,5.0};
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);
4 ferror
int ferror( FILE *stream );
六 文件缓冲区
ANSIC 标准采用**“缓冲文件系统”处理数据文件,也就是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。** 从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。 如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)
不同编译器缓冲区实现机制不一样
?
|