C++ 输入输出流:简单地介绍了【输入输出流】、【文件输入输出流】和【字符串输入输出流】。


目录

  • STL 输入输出流:整体框架
  • A) 输入、输出流
  • 1. 简介
  • 2. 格式化输出
  • 3. 流操纵算子
  • 4. cout对象的唯一性
  • B) 文件输入、输出流
  • 1. 基本使用方法
  • 2. 读入常用操作
  • C) 字符串输入、输出流
  • 1. 简介
  • 2. 对象内的 buffer
  • 3. 实现类型转换

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");

回到顶部