C 语言风格 I/O 请见C I/O。
流:从某种 IO 设备上读入或写出的字符序列。
标准库中的 IO 对象,除了 cin、cout,还有:
cerr:用来输出警告、错误给程序的使用者(可以理解为第二块屏幕)clog:用于产生 log。
# cin, cout
int ch;
cin >> ch; //ignore endline when reading
cout << endl;
# istream::getline()
getline() 会吃掉回车。
下面这种方法只能用于读 char *。
char word[50];
cin.getline(word, 50); //ignore endline when storing
而下面这种方法,把 cin 作为参数则只能用于读取 string。
string str;
getline(cin,str);
需要注意,cin.getline 是属于 iostream 的,而 getline(cin,str) 是属于 string 的。
# istream::get()
get() 有两个重载函数:
- 读一个字符
- 读一个字符串。
如果无参数,则是读字符,类似于 getchar():
ch = cin.get();
上面两个都是读取一个字符,若可用则返回它,不可用时会对 cin 设置 failbit 和 eofbit。
如果带参数,可以读 char `char *'。
char str1[n];
cin.get(str1, n, '\n')
这是读一个字符串,并且可以指定分隔符(最后分隔符会被吃掉)。
# 错误示范1
int a; char s[10];
cin >> a;
cin.getline(s);
istream& operator>> 不会吃掉最后的空格!!!
紧接在读取数字等类型的 cin 语句后,getline 会读取其前一条语句留在输入法中的 "\n"而结束。
cin >> a >> s;
而上面这个方法则没有问题。
cin会过滤空白字符,而cin.getline()不会。
# 错误示范2
int a, b, c;
cin >> a >> b;
cin >> c;
输入"1a2"时,当'a'不能输入到b时,输入流将关闭。
需要cout.flush()
# 设置输出格式
#include<iomanip>
cout << fixed; //一定先 cout <<fixed, 某 OJ 上不写会出错
cout << setprecision(n); //设置有效位数,四舍五入
cout << setw(n); //设置对齐位数
cout << setiosflags(ios::left); //设置对齐方向
resetiosflags(ios::left);
cout << hex << setiosflags(ios::showbase) << 17 << 18; //hex十六进制,oct八进制,dec十进制
# 二进制输出
函数|作用
basic_istream& read(char_type* s, std::streamsize count)|以二进制形式输入(不看是不是字符,直接莽输入)
basic_ostream& write(const char_type* s, std::streamsize count)|以二进制形式输出(失败时调用 setstate(badbit))
# 从文件输入、输出
int i;
ofstream output;
ifstream input;
output.open("output.txt");
input.open("input.txt");
input >> i;
output << i;
# fstream 的成员函数
| 函数 | 作用 |
|---|---|
void open(filename, mode) | 打开文件,详见 open() |
bool is_open() const | 判断是否打开 |
void close() | 关闭文件 |
get() | 读取一个字符或一行,详见 get() |
getline() | 读取一行(不读回车),详见 getline() |
basic_istream& read(str, std::streamsize count) | 以二进制形式输入(不看是不是字符,直接莽输入) |
basic_ostream& write(str, std::streamsize count) | 以二进制形式输出(失败时调用 setstate(badbit) |
bool eof() | 判断文件是否结束 |
# open()
void fstream::open(const char * filename, ios_base::openmode mode)
void fstream::open(const std::string & filename, ios_base::openmode mode)
其中 mode 是位掩码类型(即可以用 | 进行叠加):
| 常量 | 解释 |
|---|---|
app | 每次写入前寻位到流结尾(输出补充在文末) |
binary | 以二进制模式打开 |
in | 为读打开 |
out | 为写打开 |
trunc | 在打开时舍弃流的内容(若存在文件则覆盖) |
ate | 打开后立即寻位到流结尾(初始位置:文件末) |
mode 的默认参数:
fstream:ios_base::in | ios_base::out
ifstream:ios_base::in | ios_base::trunc
ofstream:ios_base::out
# 文件指针位置相关操作
以下 p 代表 put,对输出流操作;g 代表 get,对输入流操作。
文件指针实际上就是一个标记字节的数字,起始为 0。
用成员函数 tellg()/tellp() 获取当前位置,返回 streampos(实际上是一个整型,在 VS 2017 x86/x64 上范围和 signed long long 相同)。
修改的成员函数有绝对和相对的。下仅以 seekp() 作示范,seekg() 用法相同。
istream& seekg (streampos pos); //(1)
istream& seekg (streamoff off, ios_base::seekdir way); //(2)
(1) 修改到绝对指针,(2) 修改到相对指针,off (offset) 为位移量(可以为负),way 为位移的正负(way > 0 时往流的末尾,way == 0 时往流的开头,建议用 true/false,但既然 off 可以为负,感觉就很鸡肋了)。
# string 和 iostream 的关系
参见这篇博客。
# 重载 iostream
参考博客: http://blog.csdn.net/caroline_wendy/article/details/15336063
# 1. 输出操作符(ostream)重载
std::ostream &operator<< (std::ostream& os, const ClassA& ca);
ostream需要修改,不能复制,所以应该为非常量引用类型(nonconst &);输出类不需要修改, 应该为常量引用类型(const &);
函数有可能使用内部的私有成员,需要定义为友元(friend);
重载操作符应该为非类成员函数(nonmember function)。如果为类成员函数,则也必须为标准库成员函数,显然无法完成。
注意函数不要有格式信息(minimal formatting),为了和标准输入操作符进行统一。
# 2. 输入操作符(istream)重载
std::istream &operator>> (std::istream& is, ClassA& ca);
基本同输出操作符;
参数都为 non-const(都需要修改);
操作符函数应该包括错误恢复(error recovery),保证输入错误时,不会产生未知错误;
可以增加 I/O 条件状态(condition state)进行判断,输入错误原因。
# 刷新输入缓冲区
在读 int 的时候如果读到了非数字字符,输入流就会被关闭(输入流会变成错误状态),无法继续读入。
解决的办法就是重新打开输入流(将错误状态更改为有效状态),顺便可能需要忽略掉一些字符。
cin.clear()是将错误状态更改为有效状态cin.sync()是清除缓冲区中的未读信息cin.ignore()是忽略缓冲区中指定个数的字符,还可以指定忽略的结束符
也就是说,当我们想要刷新输入流并忽略掉已经在缓冲区的字符时,我们可以使用以下方法:
cin.clear();
cin.sync();
但是,在 Visual Studio 2019 上,他貌似并没有清楚缓冲区的未读信息。
在 Stack Overflow (opens new window) 上提到了这么一句,
cin.syncdiscards all unread characters from the input buffer. However, it is not guaranteed to do so in each implementation. Therefore,ignoreis a better choice if you want consistency.
大意是, cin.sync 在各个平台上实现的方法可能不一样,比如这里在 Visual Studio 2019 上就不能用 sync 来清楚缓冲区的未读信息。可以改为如下:
cin.clear();
cin.ignore(10000, '\n');
这样可以忽略掉缓冲区的一行字符。
注意 ignore 还可以忽略还没有输入的字符,如以下程序:
char ch;
cin.igore(10);
while (cin >> ch)
cout << ch;
运行程序,输入 qwertyuiopasdf,会输出 asdf。
# 命令行程序控制输入
命令行防止错误的输入真是令人头疼的问题了。一般有两个方案:
- 只使用
cin.getline()读入,一行一行的读 - 读错了以后马上刷新,按上面一个标题的办法