草庐IT

C++ iostream 输入输出流

bljw-02 2023-03-28 原文

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

回到顶部

有关C++ iostream 输入输出流的更多相关文章

  1. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  2. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  3. ruby - 如何进行排列以有效地定制输出 - 2

    这是一道面试题,我没有答对,但还是很好奇怎么解。你有N个人的大家庭,分别是1,2,3,...,N岁。你想给你的大家庭拍张照片。所有的家庭成员都排成一排。“我是家里的friend,建议家庭成员安排如下:”1岁的家庭成员坐在这一排的最左边。每两个坐在一起的家庭成员的年龄相差不得超过2岁。输入:整数N,1≤N≤55。输出:摄影师可以拍摄的照片数量。示例->输入:4,输出:4符合条件的数组:[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]另一个例子:输入:5输出:6符合条件的数组:[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][

  4. ruby - 将 spawn() 的标准输出/标准错误重定向到 Ruby 中的字符串 - 2

    我想使用spawn(针对多个并发子进程)在Ruby中执行一个外部进程,并将标准输出或标准错误收集到一个字符串中,其方式类似于使用Python的子进程Popen.communicate()可以完成的操作。我尝试将:out/:err重定向到一个新的StringIO对象,但这会生成一个ArgumentError,并且临时重新定义$stdxxx会混淆子进程的输出。 最佳答案 如果你不喜欢popen,这是我的方法:r,w=IO.pipepid=Process.spawn(command,:out=>w,:err=>[:child,:out])

  5. ruby - Ruby 是否使用 $stdout 来写入 puts 和 return 的输出? - 2

    我想知道Ruby用来在命令行打印这些东西的输出流:irb(main):001:0>a="test"=>"test"irb(main):002:0>putsatest=>nilirb(main):003:0>a=>"test"$stdout是否用于irb(main):002:0>和irb(main):003:0>?而且,在这两次调用之间,$stdout的值是否有任何变化?另外,有人能告诉我打印/写入这些内容的Ruby源代码吗? 最佳答案 是的。而且很容易向自己测试/证明。在命令行试试这个:ruby-e'puts"foo"'>test.

  6. ruby-on-rails - 无法在 Rails 助手中捕获 block 的输出 - 2

    我在使用自定义RailsFormBuilder时遇到了问题,从昨天晚上开始我就发疯了。基本上我想对我的构建器方法之一有一个可选block,以便我可以在我的主要content_tag中显示其他内容。:defform_field(method,&block)content_tag(:div,class:'field')doconcatlabel(method,"Label#{method}")concattext_field(method)capture(&block)ifblock_given?endend当我在我的一个Slim模板中调用该方法时,如下所示:=f.form_field:e

  7. ruby - 鸭子输入字符串、符号和数组的优雅方式? - 2

    这是针对我无法破坏的现有公共(public)API,但我确实希望对其进行扩展。目前,该方法采用字符串或符号或任何其他在作为第一个参数传递给send时有意义的内容我想添加发送字符串、符号等列表的功能。我可以只使用is_a吗?数组,但还有其他发送列表的方法,这不是很像ruby​​。我将调用列表中的map,所以第一个倾向是使用respond_to?:map。但是字符串也会响应:map,所以这行不通。 最佳答案 如何将它们全部视为数组?String的行为与仅包含String的Array相同:deffoo(obj,arg)[*arg].eac

  8. ruby-on-rails - 连接字符串时如何在 <%=%> block 内输出 html_safe? - 2

    考虑一下:现在这些情况:#output:http://domain.com/?foo=1&bar=2#output:http://domain.com/?foo=1&bar=2#output:http://domain.com/?foo=1&bar=2#output:http://domain.com/?foo=1&bar=2我需要用其他字符串输出URL。我如何保证&符号不会被转义?由于我无法控制的原因,我无法发送&。求助!把我的头发拉到这里:\编辑:为了澄清,我实际上有一个像这样的数组:@images=[{:id=>"fooid",:url=>"http://

  9. ruby - 捕获 Ruby Logger 输出以进行测试 - 2

    我有一个像这样的ruby​​类:require'logger'classTdefdo_somethinglog=Logger.new(STDERR)log.info("Hereisaninfomessage")endend测试脚本行如下:#!/usr/bin/envrubygem"minitest"require'minitest/autorun'require_relative't'classTestMailProcessorClasses当我运行这个测试时,out和err都是空字符串。我看到消息打印在stderr上(在终端上)。有没有办法让Logger和capture_io一起玩得

  10. ruby - Cucumber/Savon 省略或删除日志输出 - 2

    在运行Cucumber测试时,我得到(除了测试结果)大量调试/日志相关的输出形式:D,[2013-03-06T12:21:38.911829#49031]DEBUG--:SOAPrequest:D,[2013-03-06T12:21:38.911919#49031]DEBUG--:Pragma:no-cache,SOAPAction:"",Content-Type:text/xml;charset=UTF-8,Content-Length:1592W,[2013-03-06T12:21:38.912360#49031]WARN--:HTTPIexecutesHTTPPOSTusingt

随机推荐