C++ iostream 输入输出流

STL 输入输出流:整体框架

头文件 定义在头文件里的类 / 对象 补充说明
<istream> istream 类、 iostream istream 类是所有输入流的基类
<ostream> ostream ostream 类是所有输出流的基类
<iostream> cin 对象、 cout 对象 cinistream 类的对象;
coutostream 类的对象
<fstream> ifsream 类、 ofstream 类、 fstream
<sstream> istringstream 类、 ostringstream 类、 stringstream

回到顶部


A) 输入、输出流

*** istreamostream 类似,以下以 ostream 展开说明。

1. 简介

  • ostream = output stream

    • 是所有输出流的基类
    • 类内重载了不同基础数据类型的输出流运算符,如:intcharbool ...
    • 优势:统一了输出接口,避免输出混乱
  • STL 中定义了一个 ostream 类的对象:cout ,将数据送到标准输出流

    以下是自定义实现 ostream 的方法:

    class ostream {
    public:
        ostream& operator<< (int num) {
            printf("%d", num);
            return *this;
        }
        ostream& operator<< (const char* str) {
            printf("%s", str);
            return *this;
        }
        ...... // 重载需要输出的类型
        
    } cout; // 定义 ostream 类的对象
    
    int main() {
        cout << 21 << "happy" ; // 根据输出数据类型,调用不同的输出函数
        return 0;
    }
    

    以下是在自定义类中重载流运算符的方法:

    class MyClass {
    public:
        friend ostream& operator<< (ostream&, MyClass&);
        ......
    }
    
    ostream& operator<< (ostream& output, MyClass& x) {
        output << ... ;
        return outpur;
    }
    

    注意:第二个参数可以是不同形式,不一定是引用,如:TT&const Tconst T&

回到顶部

2. 格式化输出

  • 常用的格式化输出方式有:

    #include <iomanip>
    
    cout << fixed << 3 << " " << 3.14 << " " << 3.14159265;  
    /* 将小数的小数点固定为 6 位
       输出结果:3  3.140000  3.141593  */
    
    
    cout << setprecision(3) << 3.1415926 << 0.1054;   
    /* 设置有效数字
       输出结果:3.14  0.105  */
    
    
    cout << fixed << setprecision(2) << 3.1415;
    /* fixed 和 setprecision 合用,可以设置小数点数位
       输出结果:3.14  */
    
    
    cout << setw(5) << 921;
    /* 设置输出的宽度,一次只对一个输出有效
       默认向右对齐  */
    
    
    for(int i = 8; i <= 12; ++i) {
        cout << setw(5) << setfill('*') << i;
    }
    /* 设置输出的宽度 + 空白处填满指定字符
       输出结果:****8****9***10***11***12  */
    
    
    cout << scientific << 2018.0 << " " << 0.0001;
    /* 以科学计数形式输出
       输出结果:2.018000e+03   1.000000e-04  */
    
    
  • 以上的 fixedsetprecision() 等,包括实现换行的 endl ,都是流操纵算子

回到顶部

3. 流操纵算子

  • 借助辅助类,设置成员变量的类

  • 一些实现方式有在标准中定义,一些没有,不同编译器的实现不同

  • setprecision 实现的示例:

    class setprecision {
    private:
        int precision;
    public:
        setprecision(int p): precision(p) {}
        friend class ostream;
    }
    
    class ostream {
    private:
        int precision;
    public:
        ostream& operator<< (const setprecision &m) {
            precision = m.precision;
            return *this;
        }
    } cout;
    
    cout << setprecision(2);
    // setprecision(2) 会构造一个 setprecision 类的对象,作为参数传入输出流的函数
    
  • endl 实现的示例:

    class ostream {
        ......
        ostream& endl(ostream& os) {
            os.put('\n');     // 输出换行符
            os.flush();       // 清空缓冲区
            return os;
        }
        ostream& operator<< (ostream& (*fn)(ostream&)) {
            return (*fn)(*this);
        }
    }
    
    // 换行方法1:调用输出流+流操纵算子
    cout << endl;
    
    // 换行方法2:直接使用endl函数
    endl(cout);
    

    清空缓冲区的好处:减少外部读写次数、保证内容正确读写到文件

回到顶部

4. cout对象的唯一性

  • 定义重载流运算符 / 使用 cout 的方式:

    1. 所有重载流运算符都会返回引用 ostream&

    2. ostream 类的拷贝构造函数会手动进行删除

      ostream(const ostream& x) = delete;
      ostream(ostream&& x);
      
  • 原因:

    1. 减少复制开销
    2. 只移动不复制,只使用一个全局对象 cout 进行输出
    3. 多个对象无法同步输出状态

回到顶部


B) 文件输入、输出流

功能 补充说明
ifstream 文件输入流 从文件中读取数据 istream 的子类
在编译期间已经解析完毕
性能较好,取代了 scanf
scanf 是有写入非法内存的风险)
ofstream 文件输出流 将数据写入文件中 ostream 的子类

*** ifstreamofstream 类似,以下以 ifstream 进行说明:

回到顶部

1. 基本使用方法

#include <fstream>
#include <iostream>
using namespace std;

int main() {
    // 打开普通文本文件
    ifsream input("filename.txt");
    
    // 以二进制形式打开文件
    ifstream input("filename.bin", ifstream::binary);
    
    // 确保文件正确打开才进行操作
    if(input) { 
        ...
    }
    
    // 操作完毕,关闭文件
    input.close();
    
    return 0;
}

回到顶部

2. 读入常用操作

  • 判断是否到了文末

    while(input) {
        ...
    }
    
  • 去除前导空格

    input >> ws;
    
  • 检查下一个字符,如果到了文末就停止

    int c = input.peek();
    if(c == EOF) break;
    
  • 读取

    int n;
    input >> n;
    
    string str;
    input >> str;
    getline(cin, str);
    
    ...
    
    char sentence[1000];    
    input.getline(sentence, 1000);
    

回到顶部


C) 字符串输入、输出流

1. 简介

  • stringstream

    • iostream 的子类
    • 在对象内维护一个 buffer,可以实现输入和输出
    • 流输出函数:将数据写入 buffer
    • 流输入函数:从 buffer 读入数据
  • 基本使用方法

    #include <sstream>
    using namespace std;
    
    int main() {
        stringstream ss;
     
        ss << "10";       // 将数据写进 ss 的 buffer
        ss << "0 200";    // 会自动连接字符串
        
        int a, b;
        ss >> a >> b;     // 从 buffer 读取数据,转换成 int
        
        return 0;
    }
    

回到顶部

2. 对象内的 buffer

  • 暂存数据的空间,包含已读取和未读取的内容

  • 可以用 ss.str() 来返回 buffer 内的内容(返回类型:string)

    stringstream ss;
    ss << "10";      // buffer内有【10】
    ss << "0 200";   // buffer内有【100 200】
    
    int a, b;
    ss >> a;         // a = 100,buffer内有【100 200】
    ss >> b;         // b = 200,buffer内有【100 200】
    
    ss.str("");      // 清空 buffer
    
  • 从以上代码可见,buffer 内的内容不会因为被读取而减少

    • 有两个指针在维护着:head 和 tail
    • head:指向最后一个内容(下一个位置就是写入新内容的位置)
    • tail:指向待读取的第一个元素
    • 所有 head 在 tail 的后面,head 和 tail 之间是未读取的元素

回到顶部

3. 实现类型转换

template<class outtype, class intype>
outtype convert(intype val) {
    static stringstream ss;  // 避免重复初始化
    ss.str("");              // 清空 buffer
    ss.clear();              // 清空状态位
    ss << val;
    outtype result;          // 定义要转换的类型的变量
    ss >> result;
    return result;           // 返回转换后的内容
}

// 使用
string str = convert<string>(921);
int num = convert<int>("122");

回到顶部

posted @ 2022-05-29 13:09  是木木林丫  阅读(511)  评论(0编辑  收藏  举报