C++ 输入输出流

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;
}

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值