Reading Note of CppPrimer-Chapter8

The IO Library

IO Class

  • 常见的 IO 标准库包含 iostream, fstream, sstream;

    • iostream: istream, ostream, iostream
    • fstream: ifstream, ofstream, iofstream
    • sstream: istringstream, ostringstream, stringstream

    ch8-std-io-complete-inheritance

  • ifstream, istringstream 继承自 istream; ofstream, ostringstream 继承自 ostream; 正是因为这样的继承关系,设备类型和字符大小 (char vs wchar) 均不影响我们要执行的 IO 操作;

  • IO 类对象不能拷贝,所以一般都是以 reference 传递和返回参数;不管读取还是写入,均会改变其状态,所以不可加 const 修饰符

  • 流的状态可以用一个叫 iostate 的位集合来表示;可以操作流的 iostate 改变流的状态;

    1
    2
    3
    4
    s.eof();
    s.fail();
    s.bad();
    s.good();
    • badbit:系统级错误,如不可恢复的系统级错误,流不可使用
    • failbit:比如读取数值类型错误,流可以继续使用
    • eofbit: 比如读到文件末尾,eofbit 和 failbit 都会置位
    • goodbit : ok
  • 当流的 badbit, failbit, eofbit 置位,流会进入错误状态;由于流可能处于错误状态,因此代码中通常在使用一个流之前先检查流是不是有效的;确定流对象可用的最简单方式是把流当做条件,等价于将 s.good() 作为条件;

    1
    2
    3
    while (cin >> word){
    // ok, continue
    }
  • 刷新输出缓冲区:

    1
    2
    3
    std::cout << "Hi" << std::endl;     // 输出Hi和一个换行\n,刷新缓冲区    
    std::cout << "Hi" << std::flush; // 输出Hi,刷新缓冲区
    std::cout << "Hi" << std::ends; // 输出Hi和一个空字符\0, 刷新缓冲区
  • 如果希望在一个代码块的每次输出后都及时刷新缓冲区,可以使用 unitbuf重置到默认缓冲操作,使用 nounitbuf

    1
    2
    3
    4
    5
    std::cout << std::unitbuf; // enable automatic flushing
    {
    // in the code block, every output will be not cached and output to io directly
    }
    std::cout << std::nounitbuf; // disable automatic flushing
  • 如果程序崩溃,输出缓冲区是不会刷新的;很可能滞留在输出缓冲区里面等待打印;调试的时候可能将大量的时间浪费在追踪代码为什么没有执行上;

  • 关联输入输出流:可以将一个 istream关联到另一个 ostream,也可以将一个 ostream 关联到另一个 ostream;

    每个流最多关联一个流,但是多个流可以同时关联同一个 ostream;

    输入流关联到输出流的时候,任何输入之前都会刷新输出流;比如 std::cin 默认就关联了 std::cout,意味着用户提示信息会在输入之前打印出来;

  • stream.tie() 有两个重载的版本:

    • 不带参数的版本,返回本对象关联到的输出流(如果没有关联输出流,则返回 nullptr)
    • 带参数版本,接受一个指向 ostream 的指针,将自己关联到这个输出流
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include <fstream>
    using namespace std;

    int main () {
    ofstream ofs;
    ofs.open ("test.txt");

    // 如果按照完整的代码,会发现每次按下回车键之后都会将本次输入的内容打印到*test.txt*文件中,
    // 而如果去掉作者标注的那行代码之后,会发现只有程序运行正常结束时才会将本次输入的内容全部一次
    // 性写到test.txt文件中. 主要原因在于绑定之后每次的读取数据的操作都会刷新相关联的输出流,因此可以实现实时输出.
    cin.tie (&ofs);
    *cin.tie() << "There will be some text:";
    char c;
    while(cin >> c){
    ofs << c;
    }
    ofs.close();
    return 0;
    }
    // reference:https://www.jianshu.com/p/31ce8096fc73
  • std::cin and std::out

    std::cin

    The global objects std::cin and std::wcin control input from a stream buffer of implementation-defined type (derived from std::streambuf), associated with the standard C input stream stdin.

    These objects are guaranteed to be initialized during or before the first time an object of type std::ios_base::Init is constructed and are available for use in the constructors and destructors of static objects with ordered initialization (as long as <iostream> is included before the object is defined).

    Unless sync_with_stdio(false) has been issued, it is safe to concurrently access these objects from multiple threads for both formatted and unformatted input.

    Once std::cin is constructed, std::cin.tie() returns &std::cout, and likewise, std::wcin.tie() returns &std::wcout. This means that any formatted input operation on std::cin forces a call to std::cout.flush() if any characters are pending for output.

    std::cout

    The global objects std::cout and std::wcout control output to a stream buffer of implementation-defined type (derived from std::streambuf), associated with the standard C output stream stdout.

    These objects are guaranteed to be initialized during or before the first time an object of type std::ios_base::Init is constructed and are available for use in the constructors and destructors of static objects with ordered initialization (as long as <iostream> is included before the object is defined).

    Unless sync_with_stdio(false) has been issued, it is safe to concurrently access these objects from multiple threads for both formatted and unformatted output.

    By specification of std::cin, std::cin.tie() returns &std::cout. This means that any input operation on std::cin executes std::cout.flush() (via std::basic_istream::sentry‘s constructor). Similarly, std::wcin.tie() returns &std::wcout.

    By specification of std::cerr, std::cerr.tie() returns &std::cout. This means that any output operation on std::cerr executes std::cout.flush() (via std::basic_ostream::sentry‘s constructor). Similarly, std::wcerr.tie() returns &std::wcout. (since C++11)

File Stream

  • 对于文件流,因为调用 open 可能会失败,所以对 open 是否成功的检测通常是个好习惯;对于一个已经 open 的文件流再次调用 open 函数,会导致 failbit 置位

    1
    2
    3
    4
    5
    std::ofstream out;            // 输出文件流未和任何文件关联
    out.open(ifile); // 打开指定文件
    if (out){ // 检测open是否成功
    // do something here
    }
  • 创建文件流,可以通过构造的时候指定输出文件,也可以后续再打开;如果打开失败,则 failbit 会置位;为了关联别的文件,必须先关闭再关联

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // bind file when constructed
    std::ofstream of("test.txt", std::ios::out);

    // bind file later
    std::ofstream of1;
    of1.open("test1.txt", std::ios::out);

    // when open failed, one should close the stream firstly and open again
    if(!of1){
    of1.close();
    of1.open("test2.txt", std::ios::out);
    }
  • ifstream 的默认模式是 in,ofstream 的默认模式是 out;

    默认情况下,即使没有指定 trunc,以 out 模式打开的文件默认也会被截断

    只有 app 和 in 两种模式不会对文件截断

    ate 和 binary 可以用于任何类型的文件流

    1
    2
    3
    4
    5
    6
    static constexpr openmode app = /*implementation defined*/         // append
    static constexpr openmode binary = /*implementation defined*/ // binary
    static constexpr openmode in = /*implementation defined*/ // input
    static constexpr openmode out = /*implementation defined*/ // output
    static constexpr openmode trunc = /*implementation defined*/ // truncate
    static constexpr openmode ate = /*implementation defined*/ // goto file end

String Stream

  • stringio:istringstream, ostringstream;

    1
    2
    3
    4
    5
    std::sstream strm;        
    std::sstream strm(str); // explicit construct a sstream from string

    strm.str(); // return saved string
    strm.str(str); // copy string s to strm and return void
  • istringstream 可以像标准输入一样,使用 >> 来读取单个单词,以空格分开;

    ostringstream 可以使用 << 将要输出的东西暂存,最后一次性输出;stringstream 可以直接从 string 构建;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // in file.txt
    // morgan 201545689 12398734
    // bobstv 125232145

    std::ifstream file("file.txt");
    std::string line = "";
    while (getline(file, line)){
    std::istringstream record(line);
    std::string name = "";
    std::vector<std::string> words = {};
    record >> name; // split by blank space
    while(record >> word){
    words.push_back(word);
    }
    }