文章目录
1、输入输出机制
流:字节序列,数据间的传输操作。
1.1、流的类型
- 标准 IO:系统指定的标准设备,iostream: istream, ostream, iostream
- 文件 IO:磁盘文件,fstream: ifstream, ofstream, fstream
- 字符串 IO:内存空间,sstream: istringstream, ostringstream, stringstream
1.2、流的继承图
ios是抽象基类,由它派生出 istream 类和 ostream 类,iostream 类支持输入输出操作,iostream类是从 istream 类和 ostream 类通过多重继承而派生的类。类ifstream继承了类 istream,类ofstream 继承了类 ostream,类 fstream 继承了类 iostream.
1.3、流的状态
-
badbit:
bad()
,系统级的错误,不可恢复。 -
failbit:
fail()
,可恢复的错误。 -
eofbit:
eof()
,到达文件的结束位置。 -
goodbit:
good()
,流正常状态
管理流的状态
bool bad() const; //若流的badbit置位,则返回true;否则返回false
bool fail() const; //若流的failbit或badbit置位,则返回true;
bool eof() const; //若流的eofbit置位,则返回true;
bool good() const; //若流处于有效状态,则返回true;
iostate rdstate() const; //获取流的状态
void setstate(iostate state); //设置流的状态
//clear的无参版本会复位所有错误标志位*,重置流的状态
void clear(std::ios_base::iostate state = std::ios_base::goodbit);
1.4、流的操作
/* ----输入流操作---- */
// 读取一个字符
int_type get();
istream & get(char_type & ch);
// 读取一行数据
istream & getline(char_type * s, std::streamsize count, char_type delim ='\n');
// 读取 count 个字节的数据
istream & read(char_type * s, std::streamsize count);
// 最多获取 count 个字节,返回值为实际获取的字节数
std::streamsize readsome(char_type * s, std::streamsize count);
// 清空缓冲区:读取到前 count 个字符或在该过程中遇到 delim 字符就停止,并把读取数据
istream & ignore(std::streamsize count = 1, int_type delim = Traits::eof());
// 查看输入流中的下一个字符, 但是并不将该字符从输入流中取走,不会跳过输入流中的空格、回车符; 在输入流已经结束的情况下,返回 EOF。
int_type peek();
// 获取当前流中游标所在的位置
pos_type tellg();
// 偏移游标的位置
basic_istream & seekg(pos_type pos);
basic_istream & seekg(off_type off, std::ios::seekdir dir);
/* ----输出流操作---- */
// 往输出流中写入一个字符
ostream & put(char_type ch);
// 往输出流中写入count个字符
ostream & write(const char_type * s, std::streamsize count);
// 获取当前流中游标所在的位置
pos_type tellp();
// 刷新缓冲区
ostream & flush();
// 偏移游标的位置
ostream & seekp(pos_type pos);
ostream & seekp(off_type off, std::ios_base::seekdir dir);
1.5、缓冲区
缓冲区的类型
- 全缓冲:填满缓冲后,执行 IO 操作,文件读写
- 行缓冲:遇到换行符,执行 IO 操作,键盘输入
- 不缓冲:cerr/stderr,立即显示错误信息。
2、标准IO
cin, cout, cerr 分别用文件描述符 0,1,2 代表。
2.1、标准输入
标准输入 cin:流提取符>>,从输入流中提取数据跳过空白字符(空格、tab、换行等)。换行符将数据送入缓冲区,形成输入流。
例:cin 与流状态的使用
clear
重置流的状态ignore
清空缓冲区,ignore(std::numeric_limits<std::streamsize>::max(), '\n')
int n = 0;
cin >> n;
while (cin >> n, !cin.eof()) { // ctrl+D,流的状态:eofbit=1
// 不可恢复错误,流不能继续使用,流的状态: badbit=1
if (cin.bad()) {
break;
}
// 可恢复错误,重置流的状态 + 清空缓冲区。流的状态: failbit=1
else if (cin.fail()) {
// 1、重置流的状态,流恢复正常
cin.clear();
// 2、清空缓冲区
// 流虽然恢复正常,但是其中的杂质数据还在缓冲区,会影响之后的输出
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
// 流正常。流的状态: goodbit=1
else {
cout << n << endl;
}
}
2.2、标准输出
标准输出流
- 标准输出 cout
- 标准错误 cerr(非缓冲)
- 标准错误 clog(带缓冲)
输出缓冲区被刷新的时机
- 程序正常结束
- 缓冲区满:默认1024字节
- 使用操作符显示刷新缓冲区
- endl: 完成换行,并刷新缓冲区
- flush: 直接刷新缓冲区
3、文件IO
3.1、文件的模式
输入模式
- in:输入,文件将允许做读操作;如果文件不存在,打开失败
- ate:末尾,读操作始终发生在文件的末尾
输出模式
- out:输出,文件将允许做写操作;如果文件不存在,则直接创建一个
- app:追加,写入将始终发生在文件的末尾
- trunc:截断,如果打开的文件存在,其内容将被丢弃,其大小被截断为零
- binary:二进制,读取或写入文件的数据为二进制形式
3.2、文件流
文件流的构造函数
// ifstream 文件输入流,默认in
explicit ifstream(const char *filename, openmode mode = in);
explicit ifstream(const string &filename, openmode mode = in);
// ofstream 文件输出流,默认out
explicit ofstream(const char *filename, openmode mode = out);
explicit ofstream(const string &filename, openmode mode = out);
// fstream 文件输入输出流,默认in|out
explicit fstream(const char *filename, openmode mode = in|out);
explicit fstream(const string &filename, openmode mode = in|out);
例如:
std::ofstream ofs("text.txt"); // 默认 out 模
std::ofstream ofs("text.txt", std::ios::app); // 设置 app 模式
3.3、文件指针
查找文件指针的位置:tellp/tellg,p = put, g = get
设置文件指针的位置:seekp/seekg,文件指针的位置有
- std::ios::beg
- std::ios::cur
- std::ios::end
4、字符串IO
字符串流的构造函数
// istringstream 字符串输入流
explicit istringstream(openmode mode = ios_base::in);
explicit istringstream(const string& str, openmode mode = ios_base::in);
// ostringstream 字符串输出流
explicit ostringstream(openmode mode = ios_base::out);
explicit ostringstream(const string& str, openmode mode = ios_base::out);
// stringstream 字符串输入输出流
explicit stringstream(openmode mode = ios_base::in|ios_base::out);
explicit stringstream(const string& str, openmode mode =
ios_base::in|ios_base::out);
5、实例
例1:统计一篇英文 (The_Holy_Bible.txt) 文章中出现的单词和词频,
- 输入:某篇文章的绝对路径
- 输出:词典(词典中的内容为每一行都是一个"单词 词频")
#include <time.h>
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <algorithm>
using std::cout;
using std::endl;
using std::string;
using std::vector;
using std::ifstream;
using std::ofstream;
using std::cerr;
using std::istringstream;
using std::sort;
// 记录词频,用 hash 替代
struct Record {
Record(const string &word, int frequency)
: _word(word)
, _frequency(frequency)
{}
string _word;
int _frequency;
};
bool operator<(const Record &lhs, const Record &rhs) {
return lhs._word < rhs._word;
}
class Dictionary {
public:
Dictionary(int capa) {
_dict.reserve(capa);
}
// 读文件
void read(const std::string & filename) {
ifstream ifs(filename);
if(!ifs) {
cerr << "the ifstream " << filename << " error" << endl;
return;
}
string line;
// 每次读取文件的一行,while(getline(ifs), line)
while(getline(ifs, line)) {
istringstream iss(line);
string word;
// 读取一行中的每一个单词
while(iss >> word, !iss.eof()) {
string newWord = dealWord(word); //处理异常单词
insert(newWord); //将新的单词插入vector
}
}
// 对单词排序
sort(_dict.begin(), _dict.end());
ifs.close();
}
// 处理结果,写文件
void store(const std::string & filename) {
ofstream ofs(filename);
if(!ofs) {
cerr << "ofstream is not good" << endl;
return;
}
// 处理输出文件格式
for(size_t idx = 0; idx != _dict.size(); ++idx) {
ofs << _dict[idx]._word << " : " << _dict[idx]._frequency << endl;
}
ofs.close();
}
// 处理异常单词,不统计非单词的词频,例如123abc
string dealWord(const string &word) {
for(size_t idx = 0; idx != word.size(); ++idx) {
if(!isalpha(word[idx])) {
return string();
}
}
return word;
}
void insert(const string &word) {
if(word == string()) {
return;
}
// 查找该单词是否出现,出现则增加词频
size_t idx = 0;
for(idx = 0; idx != _dict.size(); ++idx) {
if(word == _dict[idx]._word) {
++_dict[idx]._frequency;
break;
}
}
// 单词第一次出现,加入词典
if(idx == _dict.size()) {
_dict.push_back(std::move(Record(word, 1)));
}
}
private:
vector<Record> _dict;
};
int main(int argc, char **argv) {
Dictionary dictionary(13000);
cout << "start reading..." << endl;
time_t beg = time(NULL);
dictionary.read("The_Holy_Bible.txt");
time_t end = time(NULL);
cout << "cost time: " << (end - beg) << "s" << endl;
cout << "finish reading..." << endl;
dictionary.store("data.txt");
return 0;
}
例2:查询某一单词在一篇英文 (The_Holy_Bible.txt) 文章出现的行号和行信息
#include <iostream>
#include <memory>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <fstream>
#include <sstream>
using std::cout;
using std::cin;
using std::endl;
using std::shared_ptr;
using std::string;
using std::map;
using std::set;
using std::vector;
using std::ifstream;
using std::istringstream;
using line_no = vector<string>::size_type;
// 类的前向声明,作为函数query的返回类型
class QueryResult;
// 文本查询类
class TextQuery {
public:
TextQuery(ifstream &ifs);
QueryResult query(const string &sought) const;
private:
shared_ptr<vector<string>> file; // 输入文件
map<string, shared_ptr<set<line_no>>> wm; // 单词-行号集合
};
// 查询结果类
class QueryResult {
public:
QueryResult(string s, shared_ptr<set<line_no>> p, shared_ptr<vector<string>> f)
: sought(s)
, lines(p)
, file(f)
{}
/* friend std::ostream &print(std::ostream &os, const QueryResult &rhs); */
friend std::ostream &operator<<(std::ostream &os, const QueryResult &rhs);
private:
string sought; // 要查询的单词
shared_ptr<set<line_no>> lines; // 行号集合
shared_ptr<vector<string>> file; // 输入的文件
};
// 获取文本中每个单词对应的行号集合
TextQuery::TextQuery(ifstream &ifs)
: file(new vector<string>) {
string text;
// 获取文本的每一行
while(getline(ifs, text)) {
file->push_back(text); // 保存此文本
int n = file->size() - 1; // 获取当前行号
// 将一行文本拆解为单词
istringstream iss(text);
string word;
while(iss >> word) {
// 若单词不在 wm 中,则在wm中添加一项
auto &lines = wm[word];
if(!lines) {
lines.reset(new set<line_no>); // 分配一个新的set
}
lines->insert(n); //将此行号插入set中
}
}
}
// 查询该单词是否出现
QueryResult TextQuery::query(const string &sought) const {
// 如果没有找到sought,返回一个指向此set的指针
static shared_ptr<set<line_no>> nodata(new set<line_no>);
// 使用find而不是下标运算符来查找单词,避免将单词添加到wm中
auto loc = wm.find(sought);
if (loc == wm.end()) {
return QueryResult(sought, nodata, file);
}
else{
return QueryResult(sought, loc->second, file);
}
}
/* std::ostream &print(std::ostream &os, const QueryResult &rhs) */
std::ostream &operator<<(std::ostream &os, const QueryResult &rhs) {
os << rhs.sought << " occurs " << rhs.lines->size() << " "
<< ((rhs.lines->size() > 1) ? "times" : "time") << endl;
for(auto num : *rhs.lines) {
os << "\t(line " << num + 1 << ") "
<< *(rhs.file->begin() + num ) << endl;
}
return os;
}
void runQueries(ifstream &infile) {
TextQuery tq(infile);
while(1) {
cout << "enter word to look for, or q to quit:";
string s;
if(!(cin >> s) || s == "q")
break;
/* print(cout, tq.query(s)) << endl; */
/* print(cout, tq.query(s)) << endl; */
cout << tq.query(s) << endl;
}
}
int main(int argc, char **argv) {
ifstream ifs("The_Holy_Bible.txt");
runQueries(ifs);
return 0;
}