1. 流
- 流:数据从一个对象到另一个对象的传输。
- 功能:标准输入输出+文件处理
分类 | 含义 |
---|---|
文本流 | 一串ASCII字符 |
二进制流 | 一串二进制 |
2. 流类型
标准库定义了三大类流类型:标准I/O流、文件流、字符串流
-
标准I/O流
- ios是抽象类
- ostream是cout、clog、cerr的类
- istream是cin的类
-
文件流类型
- ifstream从文件读取数据
- ofstream向文件写入数据
- iofstream文件读写数据
-
字符串流类型
- istringstream从string读取数据
- ostringstream向string写入数据
- iostringstream读写string数据
3. 流对象
通常标准I/O流对象是全局对象不需要定义,而文件流对象和字符串流对象需要用户定义。
标准I/O流对象有以下四个:
No. | 全局流对象 | 名称 | 缓存 |
---|---|---|---|
1 | cout | 标准输出流 | 带缓存 |
2 | cin | 标准输入流 | 带缓存 |
3 | clog | 标准日志流 | 带缓存 |
4 | cerr | 标准错误流 | 无缓存 |
注意:流对象通常都不能复制。
4. 流对象状态
流对象状态在某一个时刻必定处于以下四个状态之一。
No. | 状态 | 含义 |
---|---|---|
1 | good() | 前一个流操作成功 |
2 | eof() | 到输入尾/文件尾 |
3 | fail() | 发生意外事情(读取失败) |
4 | bad() | 发生意外严重事情(磁盘读取失败) |
5. I/O操作
I/O操作主要有如下五种:
-
输入操作:in >> x或者getline(in,s)
-
输出操作:out << x
-
操作符
-
流状态
-
格式化
-
输出流默认设置
类型 | 进制 | 宽度 | 对齐 | 填充 | 精度 |
---|---|---|---|---|---|
整数 | 十进制 | 0 | 右对齐 | 空格 | 1 |
实数 | 十进制 | 0 | 右对齐 | 空格 | 6位数 |
字符串 | - | 0 | 右对齐 | 空格 | 字符串实际长度 |
- 格式控制
- 格式控制成员函数
流对象.格式控制函数(实参)
- 预定义格式控制函数
预定义格式控制函数(实参)
- 流的输出控制格式
作用 | 格式控制成员函数 | 预定义格式控制函数 | 预定义格式控制符/操作子 | 效果持续 |
---|---|---|---|---|
进制 | flags() setf() unsetf() | setiosflags() | dec oct hex showbase | 能持续 |
宽度 | width(n) | setw(n) | - | 不能持续 |
对齐 | flags() setf() unsetf() | setiosflags() | right left internal | 能持续 |
填充 | fill© | setfill© | - | 能持续 |
精度 | precision(n) | setprecision(n) | - | 能持续 |
- 流的输出控制格式:dec oct hex
- 数据输入成员函数
字符输入成员函数:get()
字符串输入成员函数:getline() - 数据输出成员函数:put()
- 完整案例
#include <iostream>
using namespace std;
int main(){
int n = 10;
float f = 0.1;
string s = "Hello";
cout.width(10); // 设置宽度为10
cout.fill('*');
cout.flags(ios::hex); // 设置为8进制输出
cout << n << endl;
cout.width(10);
cout << 64 << endl;
cout.width(10);
cout << f << endl;
cout.width(10);
cout << s << endl;
cout.precision(2);
cout.flags(ios::fixed); // 保留小数点后几位有效数字
cout << 1113.14141421 << endl;
}
- 预定义格式的控制方法
a. 头文件
#include <iomanip> // setw() setfill()
b. 宽度为10,空白处已*填充,以八进制输出,左对齐
cout << setw(10) << setfill('*') << hex << left << n << endl;
// 宽度为10,空白处已*填充,以八进制输出,左对齐
c. 保留小数点后两位有效数字
cout << setprecision(2) << fixed << 1113.1415926 << endl;
// 保留小数点后两位有效数字
- 完整案例
#include <iostream>
#include <iomanip> // setw() setfill()
using namespace std;
int main(){
int n = 10;
float f = 0.1;
string s = "Hello";
cout << setw(10) << setfill('*') << hex << left << n << endl;
// 宽度为10,空白处已*填充,以八进制输出,左对齐
cout << setw(10) << 64 << endl;
cout << setw(10) << f << endl;
cout << setw(10) << s << endl;
cout << setprecision(2) << fixed << 1113.1415926 << endl;
// 保留小数点后两位有效数字
}
6. 对齐方式
flag | manipulator | 作用 |
---|---|---|
ios::left | left | 居左 |
ios::right | right | 居右 |
ios::internal | internal | 输出符号或进制后填充 |
- 成员函数方式
#include <iostream>
using namespace std;
int main(){
int n = -11;
cout.width(6);
cout.flags(ios::right);
cout << n << endl;
cout.width(6);
cout.flags(ios::left);
cout << n << endl;
cout.width(6);
cout.flags(ios::internal);
cout << n << endl;
}
- 操作子方式
#include <iostream>
#include <iomanip>
using namespace std;
int main(){
int n = -11;
cout << setw(6) << right << n << endl;
cout << setw(6) << left << n << endl;
cout << setw(6) << internal << n << endl;
}
- 混合方式
#include <iostream>
using namespace std;
int main(){
int n = -11;
cout.width(6); cout << left << n << endl;
cout.width(6); cout << right << n << endl;
cout.width(6); cout << internal << n << endl;
}
7. 整数输出格式
flag | manipulator | 作用 | 是否默认 |
---|---|---|---|
ios::dec | dec | 十进制 | 是 |
ios::oct | oct | 八进制 | 否 |
ios::hex | hex | 十六进制 | 否 |
ios::uppercase | uppercase | 使用大写输出十六进制 | 否 |
ios::showbase | showbase | 输出带有进制的字符 | 否 |
- 成员函数方式
#include <iostream>
using namespace std;
int main(){
int n = 11;
cout.flags(ios::dec);
cout << n << endl;
cout.flags(ios::hex);
cout << n << endl;
cout.flags(ios::oct);
cout << n << endl;
cout.flags(ios::showbase|ios::dec);
cout << n << endl;
cout.flags(ios::showbase|ios::oct);
cout << n << endl;
cout.flags(ios::showbase|ios::hex);
cout << n << endl;
cout.flags(ios::showbase|ios::uppercase|ios::dec);
cout << n << endl;
cout.flags(ios::showbase|ios::uppercase|ios::oct);
cout << n << endl;
cout.flags(ios::showbase|ios::uppercase|ios::hex);
cout << n << endl;
}
- 操作子方式
#include <iostream>
using namespace std;
int main(){
int n = 11;
cout << dec << n << endl;
cout << hex << n << endl;
cout << oct << n << endl;
cout << showbase << dec << n << endl;
cout << oct << n << endl;
cout << hex << n << endl;
cout << uppercase << dec << n << endl;
cout << oct << n << endl;
cout << hex << n << endl;
}
8. 浮点数输出格式
flag | 作用 | 是否默认 | 精度 |
---|---|---|---|
ios::defaultfloat | 默认浮点数格式 | 是 | 最多保留多少位数字 |
ios::scientific | 科学计数法输出浮点数 | 否 | 小数点后最多保留多少位数字 |
ios::fixed | 定点数方式输出实数 | 否 | 小数点后最多保留多少位数字 |
如果浮点数没有小数时默认不显示小时点,使用ios::showpoint可以强制输出浮点数时,必须带小数点。
定点数方式比浮点数方式更精确
取浮点数精确度时,设置ios::fixed
- 成员函数方式
#include <iostream> // cout, std::fixed, std::scientific
using namespace std;
int main () {
double a = 3.1415926534;
double b = 2006.0;
double c = 1.0e-10;
cout.precision(5);
cout << "default:\n";
cout << a << endl << b << endl << c << endl;
cout << "fixed:\n";
cout.flags(ios::fixed);
cout << a << endl << b << endl << c << endl;
cout << "scientific:\n";
cout.flags(ios::scientific);
cout << a << endl << b << endl << c << endl;
return 0;
}
- 操作子方式
#include <iostream> // std::cout, std::fixed, std::scientific
#include <iomanip>
using namespace std;
int main () {
double a = 3.1415926534;
double b = 2006.0;
double c = 1.0e-10;
cout << setprecision(5)
<< "default:\n"
<< a << endl << b << endl << c << endl;
cout << "fixed:\n" << fixed
<< a << endl << b << endl << c << endl;
cout << "scientific:\n" << scientific
<< a << endl << b << endl << c << endl;
return 0;
}
- 混合方式
#include <iostream> // std::cout, std::fixed, std::scientific
int main () {
double a = 3.1415926534;
double b = 2006.0;
double c = 1.0e-10;
std::cout.precision(5);
std::cout << "default:\n";
std::cout << a << '\n' << b << '\n' << c << '\n\n'
std::cout << "fixed:\n" << std::fixed;
std::cout << a << '\n' << b << '\n' << c << '\n\n'
std::cout << "scientific:\n" << std::scientific;
std::cout << a << '\n' << b << '\n' << c << '\n\n';
return 0;
}
9. 布尔类型输出格式
flag | manipulator | 作用 | 默认 |
---|---|---|---|
ios::boolalpha | boolalpha | 把bool值以字符串true/false输出 | 否 |
10. 其它
flag | manipulator | 作用 | 默认 |
---|---|---|---|
ios::showpos | showpos | 输出十进制0或者正数时,带+号 | 否 |
- 成员函数方式
#include <iostream> // std::cout, std::showpos, std::noshowpos
using namespace std;
int main () {
int p = 1;
int z = 0;
int n = -1;
cout.setf(ios::showpos); cout << p << '\t' << z << '\t' << n << endl;
cout.unsetf(ios::showpos); cout << p << '\t' << z << '\t' << n << endl;
return 0;
}
- 操作子方式
#include <iostream> // std::cout, std::showpos, std::noshowpos
using namespace std;
int main () {
int p = 1;
int z = 0;
int n = -1;
cout << showpos << p << '\t' << z << '\t' << n << endl;
cout << noshowpos << p << '\t' << z << '\t' << n << endl;
return 0;
}
复数虚部 a+bi 使用 showpos 非常合适。
- 问题
- 浮点数八进制/十六进制输出结果?
- 下列代码为什么能转化成十六进制?
#include <iostream>
#include <iomanip> // setiosflags() , ios::hex
using namespace std;
int main(){
cout << setiosflags(ios::hex) << 10 << endl;
}
最新版C++的iostream库中,在使用setiosflags()前要先使用resetiosflags()清除旧有相应信息。
cout << resetiosflags(ios::basefield)<< setiosflags(ios::hex) << 10 << endl;
以下三类格式化在使用 setiosflags() , 要先使用 resetiosflags() 清除旧有相应信息。
- stringstream
- 字符串转化数字
istringstream iss(str);
int n;
iss >> n;
-
C++11提供如下函数简化字符串转数字
-
stoi() stol() stoul() stoll() stoull()
-
stof() stod() stold()
-
数字转化字符串
int n;
ostringstream oss;
oss << n;
oss.str();
- 写成友元的写法
#include <iostream>
#include <sstream>
using namespace std;
class Simple{
int n;
public:
Simple():n(0){}
Simple(int n):n(n){}
friend ostream& operator<<(ostream& os, const Simple& s){
return os << "Simple(" << s.n << ")" << endl;
}
friend istream& operator>>(istream& is, Simple& s){
return is >> s.n;
}
};
int main(){
ostringstream oss;
oss << 4 << " " << 3.14 << " " << "abc" << endl << Simple(100) << endl;
cout << oss.str();
istringstream iss("abc 123 0.11 200");
string s;
int n;
float f;
Simple simple;
iss >> s >> n >> f >> simple;
cout << s << n << f << simple;
}
- 不写成友元的写法
#include <iostream>
#include <sstream>
using namespace std;
class Simple{
int n;
public:
Simple():n(0){}
Simple(int n):n(n){}
void SetN(int n){this->n = n;}
int GetN()const{return n;}
};
ostream& operator<<(ostream& os, const Simple& s){
return os << "Simple(" << s.GetN() << ")" << endl;
}
istream& operator>>(istream& is, Simple& s){
int n;
is >> n;
s.SetN(n);
return is;
}
int main(){
ostringstream oss;
oss << 4 << " " << 3.14 << " " << "abc" << endl << Simple(100) << endl;
cout << oss.str();
istringstream iss("abc 123 0.11 200");
string s;
int n;
float f;
Simple simple;
iss >> s >> n >> f >> simple;
cout << s << n << f << simple;
}
11. C++文件读写
文件:文件名+文件内容(有序数据集合)
文件名:字符序列
文件数据格式:二进制文件/文本文件
- C++文件操作流程
- 打开文件
- 读写文件
- 关闭文件
- 打开/关闭文件
操作 | 代码 |
---|---|
定义读文件流对象 | ifstream 读文件流对象 |
定义写文件流对象 | ofstream 写读文件流对象 |
定义读写文件流对象 | fstream 读写读文件流对象 |
成员函数打开文件 | 文件流对象.open(文件名,文件打开方式) |
构造函数打开文件 | 文件流类 对象(文件名,文件打开方式) |
判断文件是否打开 | !文件流对象 |
关闭文件 | 文件流对象.close(变量) |
- 文本文件读写
操作 | 代码 |
---|---|
提取运算符读文件 | 文件流对象 >> 变量 |
插入运算符写文件 | 文件流对象 << 变量 |
成员函数读文件 | 文件流对象.get(变量) |
成员函数写文件 | 文件流对象.put(变量) |
- 二进制文件读写
操作 | 代码 |
---|---|
读函数 | read() |
写函数 | write() |
测试文件结束 | eof() |
- 文件打开方式
类型 | 代码 |
---|---|
读 | ios::in |
写 | ios::out |
添加末尾 | ios::app |
已存在文件 | ios::nocreate |
未打开文件 | ios::noreplace |
二进制 | ios::binary |
- 文件对象指针位置函数
操作 | 代码 |
---|---|
获取读位置 | 对象.tellg() |
获取写位置 | 对象.tellp() |
设置读位置 | 对象.seekg() |
设置写位置 | 对象.seekp() |
-
函数后缀 p 表示 put(输出),后缀 g 表示 get(输入)。
-
如果文件是以 ios::app 文本追加方式打开,指针位置默认在文件结束,其他情况默认在文件开头。
-
文件对象状态函数
操作 | 代码 |
---|---|
判断文件对象状态是否正常 | 对象.good() |
重置文件对象状态 | 对象.clear() |
流式文件类型
- stream 流文件
- 文件指针 FILE*
stream流文件读写
- ifstream 文件读
ifstream fin(文件路径);
fin >> 变量
fin.close();
- ofstream 文件写
ofstream fout(文件路径);
fout << 变量
fout.close();
- fstream 文件写
fstream fs(文件路径,ios::in|ios::out|ios::app);
if(fs){
fs << 变量;
fs.seekg(ios::beg);
fs >> 变量;
fs.close();
}
- fstream 的打开模式是否创建不存在的文件
No. | 打开模式 | 是否创建不存在的文件 |
---|---|---|
1 | ios::in | 否 |
2 | ios::out | 是 |
3 | ios::in/ios::out | 否 |
4 | ios::in/ios::out/ios::app | 是 |
- 先读后写
fstream fs("test.txt",ios::in|ios::out|ios::app);
if(fs){
// 读文件
string str;
while(fs >> str){
cout << str << endl;
}
fs.clear();// 清除错误
// 写文件
while(cin >> str){
fs << str << endl;
}
}
- 其他案例
#include <iostream>
#include <fstream>
using namespace std;
int main(){
fstream fs("./test3",ios::in|ios::out|ios::app);
if(fs){
string str;
fs >> str;
cout << str << endl;
cout << fs.eof();
fs.clear(); // 清除eof状态
string str2;
cin >> str2;
fs << str2;
}
}
- 先写后读
fstream fs("test.txt",ios::in|ios::out|ios::app);
if(fs){
// 写文件
string str;
while(cin >> str)
fs << str << endl;
// 读文件
fs.seekg(ios::beg);
while(fs >> str)
cout << str << endl;
// 后续如果对fs操作,需要清空fs状态
}else{
cerr << "file not exist " << endl;
}
- 其他案例
#include <iostream>
#include <fstream>
using namespace std;
int main(){
fstream fs("./test3",ios::in|ios::out|ios::app);
if(fs){
string str;
cin >> str;
fs << str; // 每次写完文件读写指针位于文件末尾
fs.seekg(ios::beg); // 把文件读写指针移动到文件开头
string str2;
fs >> str2;
cout << str2 << endl;
fs.close();
}
}
文件指针 FILE 读写
- FILE 文件指针读
FILE* fp = fopen(文件路径,"r");
fscanf(,fp);
fclose(fp);
- FILE文件指针写
FILE* fp = fopen(文件路径,"w");
fprintf(,fp);
fclose(fp);
对象的序列化与反序列化
序列化:把对象转化成文本/二进制
反序列化:把文本/二进制转化成对象
文件重定向
freopen(文件路径,操作,标准IO)
操作:读 (r) 写(w)
- 重定向写
freopen("out.txt","w",stdout);
cout << "out data" <<endl;
- 重定向读
freopen("in.txt","r",stdin);
string str;
cin >> str;
-
几种常见的文件读取方式对比
- ifstream + fin
- freopen+cin+sync_with_stdio(false)
- FILE* + fscanf
- freopen+scanf
- freopen+cin
-
实现日志模块
freopen("test.log","w",stderr);
cerr << "FATAL";
cerr << "ERROR";
clog << "WARNING";
clog << "INFO";
clog << "DEBUG";