C++ 之 【C++的IO流】
目录
1. C++IO流
1.1 C++标准IO流
自定义类型隐式转换为内置类型
1.2 C++文件IO流
打开文件
文本文件的读写
二进制文件的读写
完整示例
1. C++IO流
C++系统实现了一个庞大的类库,其中ios为基类,其他类都是直接或间接派生自ios类
1.1 C++标准IO流
C++标准库提供了4个全局流对象cin、cout、cerr、clog,
使用cout进行标准输出,即数据从内存流向控制台(显示器)
使用cin进行标准输入,即数据通过键盘输入到程序中,
同时C++标准库还提供了cerr用来进行标准错误的输出,以及clog进行日志的输出,
从上图可以看出,cout、cerr、clog是ostream类的三个不同的对象,
因此这三个对象现在基本没有区别,只是应用场景不同
在使用时候必须要包含文件并引入std标准命名空间
注意:
1. cin为缓冲流。键盘输入的数据保存在缓冲区中,当要提取时,是从缓冲区中拿。如果一次输入过多,会留在那儿慢慢用,如果输入错了,必须在回车之前修改,如果回车键按下就无法挽回了。只有把输入缓冲区中的数据取完后,才要求输入新的数据。
2. 输入的数据类型必须与要提取的数据类型一致,否则出错。出错只是在流的状态字state中对应位置位(置1),程序继续。
3. 空格和回车都可以作为数据之间的分格符,所以多个数据可以在一行输入,也可以分行输
入。但如果是字符型和字符串,则空格(ASCII码为32)无法用cin输入,字符串中也不能有
空格。回车符也无法读入。
4. cin和cout可以直接输入和输出内置类型数据,
原因:标准库已经将所有内置类型的输入和输出全部重载了
5. 对于自定义类型,如果要支持cin和cout的标准输入输出,需要对>>、<<进行重载。
6. 在线OJ中的输入和输出:
对于IO类型的算法,一般都需要循环输入
输出:严格按照题目的要求进行,多一个少一个空格都不行
连续输入时,vs系列编译器下在输入ctrl+Z时结束
// 单个元素循环输入
while(cin>>a){// ...}// 多个元素循环输入
while(c>>a>>b>>c){// ...}// 整行接收
while(cin>>str){// ...}
7. istream类型对象转换为逻辑条件判断值
istream& operator>> (int& val);explicit operator bool() const;
实际上我们看到使用while(cin>>i)去流中提取对象数据时,调用的是operator>>,返回值是 istream类型的对象,那么这里可以做逻辑条件值,源自于istream的对象又调用了operator bool,operator bool调用时如果接收流失败,或者有结束标志,则返回false
cin.operator >>(i).operator bool();
-
自定义类型隐式转换为内置类型
class A
{
public:A(int a):_a(a){ }operator int(){return _a;}private:int _a;
};int main()
{//自定义类型<-内置类型A aa1 = 1;//内置类型<-自定义类型int a = aa1;cout << a << endl;return 0;
}
成员函数 operator int 使得自定义类型能够隐式转换为内置类型 int
explicit 修饰构造函数,禁止隐式类型转换,仍可通过显式调用构造(A aa1(1);)
explicit 修饰operator int ,禁止隐式转换(必须 int x = static_cast<int>(a))
1.2 C++文件IO流
C++根据文件内容的数据格式分为二进制文件和文本文件
采用文件流对象操作文件的一般步骤:
1. 定义一个文件流对象
- ifstream ifile(只输入用)
- ofstream ofile(只输出用)
- fstream iofile(既输入又输出用)
2. 使用文件流对象的成员函数打开一个磁盘文件,使得文件流对象和磁盘文件之间建立联系
3. 使用提取和插入运算符对文件进行读写操作,或使用成员函数进行读写
4. 关闭文件
-
打开文件
使用ifstream、ofstream和fstream类中的成员函数open打开文件
◼open函数原型如下所示
void open(constunsigned char* filename,intmode, intaccess=filebuf::openprot);⚫
第一个参数用来传递文件名;
第二个参数mode 的值决定文件的打开方式;
第三个参数决定文件的保护方式,用户通常只使用缺省值。
打开方式是已经在ios类中定义了的枚举常量,常用的有以下几种:
ios::in打开一个文件用于输入(读)操作。
ios::out打开一个文件用于输出(写)操作。
ios::ate打开一个已有的文件,文件指针指向文件尾。
ios::app以输出方式打开文件,写入的数据追加在文件末尾。
ios::trunc打开一个文件。如果文件已存在,则删除其中全部数据;如果文件不存在,则建立新文件。如果已指定了ios::out,而未指定ios::app、ios::ate、ios::in,则同时默认此方式。ios::nocreate打开一个已有的文件,如果文件不存在,则打开文件失败(即不创建一个新文件)。
ios::noreplace如果文件不存在,则建立新文件;如果文件存在,则打开失败。noreplace是不更新原有文件的意思。
ios::binary 指定文件以二进制方式打开,而不是缺省说明的文本方式
◼用构造函数打开文件的格式有以下几种:
⚫ifstream对象名("文件名")
⚫ofstream对象名("文件名")
⚫fstream对象名("文件名",打开方式)
ofstreamfout(“mytest.txt”);
上述语句相当于:
ofstreamfout; fout.open("mytest.txt");
-
文本文件的读写
对文本文件的读写操作可以使用以下两种方法:
⚫用流输出运算符“<<”和流输入运算符“>>”输入输出标准类型的数据。
⚫用文件流的put 、get 和getline等成员函数进行字符的输入输出
std::ofstream outFile("data.txt");
if (outFile) {// 写入各种类型数据outFile << "字符串" << " " << 123 << " " << 45.67 << std::endl;// 写入单个字符outFile.put('A');
}std::ifstream inFile("data.txt");
if (inFile) {// 方法1:使用 >> 运算符std::string str;int num;double d;inFile >> str >> num >> d;// 方法2:逐行读取std::string line;while (std::getline(inFile, line)) {std::cout << line << std::endl;}// 方法3:逐个字符读取char ch;while (inFile.get(ch)) {std::cout << ch;}
-
二进制文件的读写
二进制文件读写主要用istream类的成员函数read和ostream类的成员函数write来实现的。
这两个函数的原型如下所示。
istream& read( char*buffer, intlen); ostream& write(const char* buffer, intlen);
⚫其中,read() 是流类istream中的成员函数,
其功能为:从相应的流中读取len个字节,并把它们放入指针buffer 所指向的缓冲区中。
⚫由于参数buffer 的类型为char *。因此当输入其他类型数据时,必须进行类型转换。例如:intarrryi[]={100,200,300}; read((char *)&arrayi[0],sizeof(arrayi));
std::ofstream outFile("data.txt");
if (outFile) { // 写入字符数组const char* text = "C-style string";outFile.write(text, strlen(text));
}std::ifstream inFile("data.txt");
if (inFile) { //读取指定字节数(二进制模式)char buffer[100];inFile.read(buffer, sizeof(buffer));
}
完整示例
struct ServerInfo
{char _address[32];int _port;Date _date;
};
struct ConfigManager
{
public:ConfigManager(const char* filename):_filename(filename){}void WriteBin(const ServerInfo& info){ofstream ofs(_filename, ios_base::out | ios_base::binary);ofs.write((const char*)&info, sizeof(info));}void ReadBin(ServerInfo& info){ifstream ifs(_filename, ios_base::in | ios_base::binary);ifs.read((char*)&info, sizeof(info));}// C++文件流的优势就是可以对内置类型和自定义类型,都使用// 一样的方式,去流插入和流提取数据// 当然这里自定义类型Date需要重载>> 和 <<// istream& operator >> (istream& in, Date& d)// ostream& operator << (ostream& out, const Date& d)void WriteText(const ServerInfo& info){ofstream ofs(_filename);ofs << info._address << " " << info._port << " " << info._date;}void ReadText(ServerInfo& info){ifstream ifs(_filename);ifs >> info._address >> info._port >> info._date;}
private:string _filename; // 配置文件
};
int main()
{ServerInfo winfo = { "192.0.0.1", 80, { 2022, 4, 10 } };// 二进制读写ConfigManager cf_bin("test.bin");cf_bin.WriteBin(winfo);ServerInfo rbinfo;cf_bin.ReadBin(rbinfo);cout << rbinfo._address << " " << rbinfo._port << " "<< rbinfo._date << endl;// 文本读写ConfigManager cf_text("test.text");cf_text.WriteText(winfo);ServerInfo rtinfo;cf_text.ReadText(rtinfo);cout << rtinfo._address << " " << rtinfo._port << " " <<rtinfo._date << endl;return 0;
}