输入输出语义是将程序与文件和设备进行关联,并使用缓存区(一款共享的内存)在程序与文件/设备之间传输数据。
标准输入关联的是键盘设备,标准输出关联的是显示器设备。标准的意思是规定了0、1、2这三个文件描述符用来关联基本输入输出设备。
Stream特点
C++的IO称为stream,流
,流的特点如下:
- 提供buffer(缓冲区,共享的内存),在程序和文件之间传输数据;
- 字节流,以队列形式先入先出,每次一个字节;
- 格式化,将文本类型格式化为其他数据类型,或反之。
和流对应的概念是消息,流的边界是字符,消息的边界是消息结构体。因为消息结构体中存有消息类型,所以消息适合于多对多传输,而流适合于1对1传输。
C++内置了cin、cout、cerr、clog以及4个宽字符共8个流对象。
cin关联了键盘;
cout关联了屏幕;
cerr和clog也关联了屏幕。
常用的stream头文件有:
#include <istream>
#include <ostream>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iominip>
随机访问:
istream::tellg() 返回读取位置
istream::seekg() 设置读取位置
ostream::tellp() 返回写入位置
ostream::seekp() 设置写入位置
beg、cur、end 相对位置
标准IO成员函数
char a;
char buffer[100];
// 输入
cin.get(&a);
cin.getline(buffer,100,';');
cin.ignore();//丢弃一行
// 输出
cout.put(char c);
cout.flush();
格式化
#include <iominip>
// 标志位
setiosflags 设置flags
resetiosflags 清除flags
// 刷新和换行
endl 换行并刷新缓冲区
ends 追加’\0’
flush 刷新缓冲区
ws 忽略开始的空白
skipws 同ws一样
noskipws 不忽略开始的空白
unitbuf 每次写操作都刷新缓存
nounitbuf 每次写操作后不刷新缓存
// 显示宽度
setw 设置输出宽度
left 左对齐
right 右对齐
internal 正负号左对齐,数值右对齐
setfill 多余的输出宽度用指定字符填充
// 显示类型
boolalpha 用文本表示布尔值
noboolalpha 用数值表示布尔值
showpos 写出正号
noshowpos 不写出正号
showuppers 用符号表示的数值,符号大写
noshowuppers 用符号表示的数值,符号不需要大写
// 显示进制
oct 八进制
dec 十进制
hex 十六进制
showbase 显示进制前缀
// 显示浮点数
showpoint 浮点类型必须显示小数
noshowpoint 浮点类型不必须显示小数
setprecision 显示的小数个数
fixed 使用十进制显示
scientific 使用科学计数法显示
// 国际化
put_time 输出日期
get_time 读入日期
put_money 输出金额
get_money 读入金额
cin.imbue 获取locale
cout.getloc 获取locale
文件读写
C++中的文件操作头文件:
#include <ifstream>
#include <ofstream>
#include <fstream>
#include <filebuf>
文件标志位:
in 打开并读取,ifstream的默认模式
out 打开并写入,ostream的默认模式
app 追加
ate 定位光标到结尾
trunc 覆写文件
binary 二进制模式
C++中有两种打开文件的方式,一种是使用对象的构造函数,另一种是显示使用open函数。
使用构造函数打开文件,并在离开作用域后由析构函数关闭文件:
#include <fstream>
// 写入文件
ofstream file("file_name");
ofstream file("file_name", std::ios::out|std::ios::app);
file << "hello" << endl;
// 读取文件
char c;
ifstream file("file_name");
file.get(c);
使用open()打开文件:
ifstream file;
file.open("file_name");
...
file.clear();
file.close();
显然,还是面向对象的方法好用。
文本模式和二进制:
使用二进制文件模式时,程序将数据从内存传输给文件(反之亦然)时,将不会发生任何隐藏的转换,而默认的文本模式并非如此。例如,对于Windows文本文件,它们使用两个字符的组合(回车和换行)表示换行符;Macintosh文本文件使用回车来表示换行符;而UNIX和Linux文件使用换行(linefeed)来表示换行符。C++是从UNIX系统上发展而来的,因此也使用换行(linefeed)来表示换行符。为增加可移植性,Windows C++程序在写文本模式文件时,自动将C++换行符转换为回车和换行;Macintosh C++程序在写文件时,将换行符转换为回车。在读取文本文件时,这些程序将本地换行符转换为C++格式。对于二进制数据,文本格式会引起问题,因此double值中间的字节可能与换行符的ASCII码有相同的位模式。另外,在文件尾的检测方式也有区别。因此以二进制格式保存数据时,应使用二进制文件模式(UNIX系统只有一种文件模式,因此对于它来说,二进制模式和文本模式是一样的)。
string stream
#include <iostream>
#include <sstream>
ostringstream os;
os.str() 返回string
os.str(a) 设置stringstream
重定向流
通过关联/设置缓冲区,可以不必借助操作系统就完成IO重定向。
// 获取缓冲区
cout.rdbuf()
// 设置缓冲区
cout.rdbuf(file.rdbuf())
将流重定向到标准输出,并格式化:
#include <iostream>
#include <iomanip>
using namespace std;
int main(){
ostream hexout(cout.rdbuf());
hexout << hex << showbase;
//hexout.setf(ios::hex, ios::basefield);
//hexout.setf(ios::showbase);
int a = 1234;
cout << a << endl;
hexout << a << endl;
return 0;
}
1234
0x4d2
将标准输入和标准输出重定向并不安全,因为被重定向的对象在离开作用域后缓冲区会被析构,导致cout没有缓冲区可用,所以如果需要使用智能指针进行包装,使其在析构时还原rdbuf。
void redirect(ostream& strm){
auto del = [&](streambuf* p){
strm.rdbuf(p);
}
unique_ptr<streambuf,delctype(del)> origBuffer(strm.rdbuf(),del);
ofstream file(“file_name”);
strm.fdbuf(file.rdbuf());
}
redirect(out);
缓冲区
stream并不执行读写操作,而是交给stream buffer来完成。
缓冲区对象有以下成员函数:
sputc(c) 将字符c写入缓冲区
sputn(s,n) 将子串写入缓冲区
in_avail() 返回有效字符下标
sgetc() 查看当前字符
sbumpc() 获取当前字符
snextc() 跳过当前字符
sgetn() 读取n个字符
sputbackc(c) 将字符c返回缓冲区
sungetc() 返回当前字符
pubimbue() 设置区域
getloc() 获取区域
pubseekpos 设置当前位置
缓冲区迭代器可用来迭代缓冲区的字符。
所以stream负责格式化,buffer负责读写。