一,基础概念
C++的IO操作是基于字节流,并且IO操作与设备无关,同一种IO操作可以在不同类型的设备上使用。
C++的流是指流入/流出程序的字节序列,在输入操作中数据从外部设备(键盘,文件,网络等)流入程序,在输出操作中数据从程序流向外部设备(控制台,文件,网络等)。
流充当了程序与外部设备之间的管道,使程序中的具体操作独立于各种外部设备。
常用的流:基础输入输出流,文件流,字符串流。
流的实例不仅包含普通的数据,还包含表示当前位置的数据。
在C++编程中,流的使用步骤如下:
1.实例化一个流对象。
2.将流对象关联到实际的外部设备(键盘,控制台,文件,网络等)。
3.调用流对象中提供的成员函数,完成数据的转换、传输等操作。
4.断开流对象与外部设备的关联,比如关闭文件。
5.释放流对象占用的内存资源。
流具有缓冲区,大部分时候,往流中写入数据后,流并不会马上把数据输出到指定目的地,为了提高性能,流先用缓冲区将数据存储起来,缓冲区达到一定大小后再输出到指定目的地。
刷新缓冲区的条件:
遇到触发的函数,如endl。
流对象离开作用域,被析构时。
流的缓冲区被写满。
显式调用flush()函数。
流对应的头文件有<ostream>, <fstream>等。
流支持的数据类型:数值类型,指针,char类型,std::string类,C风格字符串等。
std标准库包含预定义的流的实例,有cout,cin,cerr,clog等。
二,输出流
1.输出流的定义
对应运算符:operator<<
含义:流中的数据输出到外部设备,"设备 << 程序"。
<<运算符返回的是对一个流的引用,因此,可以连续调用多次<<运算符,来连续输出多段数据。
C++流遇到C风格的转义字符,比如“\n”,可以自动做解析。
“\n”和end都可以实现换行,但是“\n”只是换行,而end还会刷新缓存。
2.输出流的原始方法
(1).输出
put():写入单个字符。
write():写入字符数组。
代码样例,输出到控制台打印:
const char* test = "hello there";
cout.write(test, strlen(test));
cout.put('a');
(2).刷新缓冲区
flush()
代码样例,显式刷新流:
cout << "abc";
cout.flush();
cout << "de";
cout << "fgh";
cout << endl;
(3).判断流是否可用
good():可用时,返回true。
代码样例:
if (cout.good()){
cout << "All good" << endl;
}
bad():发生致命错误时,返回true。
fail():最近一次操作失败时,返回true。
代码样例:
cout.flush();
if (cout.fail()){
cerr << "Unable to flush to standard out" << endl;
}
3.输出流的操作算子
以下算子可以用来格式化输出流:
endl:输出一个行结束序列,并刷新缓存。
hex、oct、dec:以十六进制、八进制、十进制输出数字。
setw:设置输出数值型数据的字段宽度。
setfill:设置用于填充的字符。
setprecision:设置输出小数时的小数位数。
代码样例:
#include <chrono>
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
int i = 123;
cout << "The result is:" << setw(6) << i << endl;
cout << "The result is:" << setfill('*') << setw(6) << i << endl;
float j = 0.123456;
cout << "The result is:" << setprecision(3) << j << endl;
}
运算结果:
The result is: 123
The result is:***123
The result is:0.123
三,输入流
1.输入流的定义
对应运算符:operator>>
含义:流中的数据从设备读入到程序中,"设备 >> 程序"。
2.输入流的原始方法
输入流也可以像输出流一样调用good()、bad()、fail()等方法,还可以调用eof()判断流的指针是否到达尾部。
(1).输入
get():读取单个字符。
read():读取字符数组。
(2).回退
unget():在读取的时候回退一个位置,将读取的前一个字符放回到流中。如果当前位置是流的起始位置,调用unget()会失败。
putback():和unget()一样支持回退,但是putback()可以指定放回的字符。
(3).预览
peek():预览调用get()后返回的下一个值。
(4).读取整行
getline():从输入流中获得一行数据,用法区别于C++中的std::getline()函数。
3.输入流的操作算子
以下算子可以用来格式化输入流:
hex、oct、dec:以十六进制、八进制、十进制读入数字。
skipws:输入时跳过空白字符,默认情况下为skipws。
noskipws:输入时读取空白字符作为标记。
代码样例:
#include <iostream>
#include <sstream>
int main()
{
char c1, c2, c3;
std::istringstream("a b c") >> std::skipws >> c1 >> c2 >> c3;
std::cout << "skipws behavior:"
" c1 = " << c1 <<
" c2 = " << c2 <<
" c3 = " << c3 << '\n';
std::istringstream("a b c") >> std::noskipws >> c1 >> c2 >> c3;
std::cout << "noskipws behavior:"
" c1 = " << c1 <<
" c2 = " << c2 <<
" c3 = " << c3 << '\n';
return 0;
}
运行结果:
skipws behavior: c1 = a c2 = b c3 = c
noskipws behavior: c1 = a c2 = c3 = b
、
四,字符串流
对应头文件:<sstream>
常用字符串流:
std::ostringstream:将数据写入字符串
std::istringstream:从字符串读取数据
std::stringstream:双向操作字符串
1.字符串流支持的模式
ios::in:进行输入操作。
ios::out:进行输出操作。
ios::app:在字符串流后面追加。
ios::trunc:截断字符串。
ios::binary:用于二进制(原始字节)IO 操作,而不是基于字符的操作。
ios::ate:将指针移动到流的末尾。
2.字符串流的常用方法
字符输入流的操作:
operator>>:格式化输入。
get:读取单个字符。
read:读取字符数组。
getline:读取整行字符。
readsome:读取若干数量的字符。
peek:预览下一个字符。
unget:读取期间,回退一个字符。
tellg:返回流中的当前操作位置。
seekg:移动到流中的指定位置。
sync:与存储设备同步。
字符输出流的操作:
operator<<:格式化输出。
put:写入单个字符。
write:写入字符数组。
tellp:返回流中的当前操作位置。
seekp:移动到流中的指定位置。
flush:刷新数据到存储设备。
和状态相关的操作:
good()、bad()、fail()、eof():前面已经讲过。
setstate:设置状态。
clear:清除状态。
3.代码样例
#include <iostream>
#include <sstream>
#include <string>
#include <map>
using namespace std;
int main()
{
string mystr = "how to study cpp very very good";
map<string, int> myMap;
stringstream ss(mystr);
string Word;
while (ss >> Word)
{
myMap[Word]++;
}
map<string, int>::iterator it;
for (it = myMap.begin(); it != myMap.end(); it++)
{
cout << it->first << " -> "
<< it->second << "\n";
}
return 0;
}
运行结果:
cpp -> 1
good -> 1
how -> 1
study -> 1
to -> 1
very -> 2
五,文件流
头文件:<fstream>
常用文件流:
std::ofstream:将数据写入文件
std::ifstream:从文件读取数据
std::fstream:双向操作文件
std::ofstream, std::ifstream文件流的析构函数会自动关闭底层文件,所以操作完文件流以后不需要显式调用close()函数。
1.文件流支持的模式
ios::in:进行输入操作。
ios::out:进行输出操作。
ios::app:在文件流后面追加。
ios::trunc:截断文件内容。
ios::binary:用于二进制(原始字节)IO 操作,而不是基于字符的操作。
ios::ate:将指针移动到流的末尾。
文件流默认以文本模式打开文件流,如果指定了ios_base::binary,文件流将以二进制模式被打开。
2.文件流的常用方法
文件输入流的操作:
operator>>:格式化输入。
get:读取单个字符。
read:读取字符数组。
getline:读取整行字符。
readsome:读取若干数量的字符。
peek:预览下一个字符。
unget:读取期间,回退一个字符。
tellg:返回流中的当前操作位置。
seekg:移动到流中的指定位置。
sync:与存储设备同步。
文件输出流的操作:
operator<<:格式化输出。
put:写入单个字符。
write:写入字符数组。
tellp:返回流中的当前操作位置。
seekp:移动到流中的指定位置。
flush:刷新数据到存储设备。
和状态相关的操作:
good()、bad()、fail()、eof():前面已经讲过。
setstate:设置状态。
clear:清除状态。
3.代码样例
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
fstream obj;
obj.open("test.txt", ios::out);
obj << "Hello World";
int pos1, pos2;
pos1 = obj.tellp();
cout << pos1 << endl;
obj.seekp(0, ios::end);
obj << "C++";
pos2 = obj.tellp();
cout << pos2 << endl;
obj.close();
}
运行结果:
11
14
六,参考阅读
《C++高级编程》
https://zh.cppreference.com/w/cpp/io/basic_stringstream
https://zh.cppreference.com/w/cpp/io/basic_ifstream
https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp10_io.html