每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE。
//VS2013编译环境提供的 stdio.h 头文件中有以下的文件类型申明:
struct _iobuf {
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
};
typedef struct _iobuf FILE;
2、文件的打开和关闭
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
FILE* pf = fopen("test.txt","r");
if (NULL == pf)
{
perror("fopen");
return 0;
}
fclose(pf);
pf = NULL;
return 0;
}
3、文件的顺序读写
①字符输出函数 fputc(适用所有输出流)
文本行输出函数 fputs(适用所有输出流)
int main()
{
FILE* pf = fopen("test.txt", "w");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//写文件
char ch = 0;
for (ch = 'a'; ch <= 'z'; ch++)
{
fputc(ch, pf);
}
//写一行数据
fputs("hello star\n", pf);
fputs("hello galaxy\n", pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
②字符输入函数 fgetc(适用于所有输入流)
文本行输入函数 fgets(适用于所有输入流)
int main()
{
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
//读一行数据
char buf[20] = {0};
fgets(buf, 20, pf);
printf("%s", buf);
fgets(buf, 20, pf);
printf("%s", buf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
③格式化输入函数 fscanf(适用于所有输入流)
格式化输出函数 fprintf(适用于所有输出流)
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { "zhangsan", 20, 95.5 };
FILE* pf = fopen("test.txt", "w");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//格式化的写入文件
fprintf(pf, "%s %d %f\n", s.name, s.age, s.score);
//格式化的读取文件
fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));
//打印看数据
printf("%s %d %f\n", s.name, s.age, s.score);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
④任何一个C语言程序运行的时候,默认打开3个流:
stdin - 标准输入(键盘)类型:FILE*
stdout - 标准输出(屏幕)类型:FILE*
stderr - 标准错误(屏幕)类型:FILE*
而进行文件操作时,要先手动打开文件。
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { 0 };
fscanf(stdin, "%s %d %f", s.name, &(s.age), &(s.score));
fprintf(stdout, "%s %d %f\n", s.name, s.age, s.score);
return 0;
}
⑤二进制输入 fread 文件
二进制输出 fwrite 文件
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { 0};
FILE* pf = fopen("test.txt", "rb");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//二进制输入
fread(&s, sizeof(struct S), 1, pf);
printf("%s %d %f\n", s.name, s.age, s.score);
//二进制输出
fwrite(&s, sizeof(struct S), 1, pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
⑥sscanf:从一个字符串中,还原一个格式化的数据
sprintf:把格式化的数据,存放在(转换成)一个字符串
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = {"zhangsan", 20, 98.5};
char buf[100] = { 0 };
sprintf(buf, "%s %d %f", s.name, s.age, s.score);
printf("%s\n", buf);//按照字符串打印的
struct S tmp = { 0 };
sscanf(buf, "%s %d %f", tmp.name, &(tmp.age), &(tmp.score));
printf("%s %d %f\n", tmp.name, tmp.age, tmp.score);//打印结构体数据
return 0;
}
4、文件的随机读写
fseek:根据文件指针的位置和偏移量来定位文件指针
ftell:返回文件指针相对于起始位置的偏移量
rewind:让文件指针的位置回到文件的起始位置
int main()
{
FILE*pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
}
else
{
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
//fseek(pf, -2, SEEK_CUR);//从当前位置读取b
fseek(pf, 1, SEEK_SET);//从起始位置读取b
ch = fgetc(pf);
printf("%c\n", ch);//b
printf("%d\n", ftell(pf));
rewind(pf);
ch = fgetc(pf);
printf("%c\n", ch);//a
}
return 0;
}
5、文件读取结束的判定
首先文件读取结束了,结束后想知道读取结束的原因:
feof-返回真,就说明是文件正常读取到了结束标志而结束的。
ferror-返回真,就说明是文件在读取过程中出错了,而结束。
#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);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
二、C++:
1、C++的io流
①运算符重载(operator<<和operator>>)
②自动识别类型(函数重载)
③面向对象可以更好支持自定义类型更形象(cout << x)
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<string>
using namespace std;
//istream& operator>> (int& val);
//explicit operator bool() const;
int main()
{
string str;
while (cin >> str) // operator>>(cin, str).operator bool()
{
cout << str << endl;
}
return 0;
}
2、C++文件的io流
①C语言与C++的二进制/文本读写
class Date
{
friend ostream& operator << (ostream& out, const Date& d);
friend istream& operator >> (istream& in, Date& d);
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
operator string()
{
string str;
str += to_string(_year);
str += ' ';
str += to_string(_month);
str += ' ';
str += to_string(_day);
return str;
}
operator bool()
{
// 这里是随意写的,假设输入_year为0,则结束
if (_year == 0)
return false;
else
return true;
}
private:
int _year;
int _month;
int _day;
};
istream& operator >> (istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
ostream& operator << (ostream& out, const Date& d)
{
out << d._year << " " << d._month << " " << d._day;
return out;
}
#include<fstream>
int main()
{
Date d(2023, 10, 14);
//C语言
FILE* fin = fopen("file.txt", "w");
// 二进制的方式写入
fwrite(&d, sizeof(Date), 1, fin);
fclose(fin);
//文本的方式写入:调用operator string
string str = d;
fputs(str.c_str(), fin);
//C++
ofstream ofs("file.txt", ios_base::out | ios_base::binary);//二进制覆盖写入
ofstream ofs("file.txt");
// 二进制的方式写入
ofs.write((const char*)&d, sizeof(d));
// 文本的方式写入:ofstream是ostream的派生类,故ofstream也可调用operator<<
ofs << d;
return 0;
}
②C语言的sprintf和sscanf与C++的ostringstream和istringstream
#include<sstream>
int main()
{
//C语言:sprintf与sscanf
int x;
cin >> x;
char buff[128];
sprintf(buff, "int:%d", x);
//C++:ostringstream与istringstream
// 简单序列化和反序列化
Date d(2023, 10, 16);
ostringstream oss;
oss << d;
string str = oss.str();
cout << str << endl;//ostringstream是ostream的派生类,故ostringstream也可调用operator<<
string str("2023 10 16");
istringstream iss(str);
Date d;
iss >> d;//istringstream是istream的派生类,故istringstream也可调用operator<<
return 0;
}