UP | HOME

Input and Output in C++

Overview

These notes provide an overview for how to perform input and output in C++, focused on best practices (as of C++23).

Format

What

  1. Create strings and output them using a syntax similar to the Python Format Specification.
  2. The preferred method for string formatting and output as of C++23.
  3. Handles output but does not handle input.

How

  1. #include<print> (use #include<format> if only formatting strings but not outputting them).
  2. Uses the Formatting Library, available since C++20
  3. std::format("Var1 = {} and var2 = {}", var1, var2)
    • Returns a string where each {} is replaced with a string representation of the corresponding argument
    • The {} can have {arg-id (optional) : format-spec}
      • arg-id is an optional index specifying which argument to std::format is substituted.
        • std::format("var2 = {1} var1 = {0}", var1, var2)
        • You must either not use arg-id for any {} or use it for all {}.
      • format-spec provides options for formatting, such as printing in hex format or how many decimal digits to include.
  4. In C++23 std::print and std::println allow printing with format strings directly
    • In C++20 you can format strings with std::format and then output those strings
    • In C++11 the third-party fmt library (upon which std::format is based) can be used if desired
    • It is possible to use std::print and std::println to output to files by

File I/O

  1. std::print and std::println can output to both C++ std::ostream objects or C FILE* pointers.
    • = #include<ostream> to use the std::ostream version.
  2. The advantage of using FILE* is efficiency, however, you need to explicitly close the FILE* when done.
    • It can be wrapped using a unique_ptr with a custom deallocator: std::unique_ptr<std::FILE, int(*)(FILE*)>(fopen("filename", "w"), &std::fclose)
  3. The advantage of using an ofstream is that it will close automatically due to it's destructor
  4. Explicitly closing the file is needed if you want to be able respond to errors that may occur when closing the file, regardless of whether you use a FILE*, a unique_ptr<FILE, int(*)(FILE*)> or std::ostream
    • The behavior of std::ostream is to fail silently if the file closure fails in the destructor
    • Of course there is not much that can be done on a file-close error.

Why

  1. Safety: if the format string is specified incorrectly or the type is not std::formattable the compiler will catch the error
  2. Extensibility: it is possible to enable custom types to work with std::format and provide custom formatting options
  3. Readability: the std::format is based on syntax similar to Python f-strings and remains local to each variable you wish to output

Iostreams

What

  1. The original method for input and output in C++
  2. Handles both input and output, so it is what you should use for input

How

Output

  1. #include<iostream>
  2. To output cout << "Stuff" << myvar << " stuff" << endl
    • endl outputs a newline and flushes the output buffer (ensuring all text is displayed on the screen)
    • To output a newline without flushing the buffer use "\n" in the string
    • Flushing the buffer takes time, so it's best to use "\n" until you want to ensure that all the output is written to the screen.
    • Variables and strings can all be output together by separating with <<
  3. Formatting is accomplished using #include<iomanip>
    • This header contains input/output manipulators that change properties of the stream
    • Using these are notoriously tricky because they change the state of the stream
    • For example cout << "Int a in hex: 0x" << std::hex << myint << endl.
      • After executing this line, cout will output integers in hex until std::dec is inserted into the stream
  4. std::cout is the standard output stream. There is also std::cerr for the standard error stream, and files can be opened as an std::ofstream and used the same way.
  • File Output
    1. Create an std::ofstream object, and use it like cout
    2. ostreams can be open in binary mode and raw bytes writtend directly with the write member function.

Input

  1. The global istream std::cin is automatically connected to the standard input stream, allowing you to read buffered keyboard input
  2. std::cin >> var reads data from the standard input into a variable
    • The type of var determines how the text is parsed
    • Spaces separate values, and \n (the user typing Enter) causes the buffer to be read and parsed
    • Parsing errors set error flags on std::cin which need to be checked manually
    • It is possible to call std::cin.exceptions() to make errors throw exceptions, but likely not a good idea because running into an error when parsing user input is not exceptional behavior.
  3. Use std::string line; std::getline(std::cin, line) to read a whole line of text and parse it yourself.
  • File Input
    1. Create an std::ifstream to read from a file
    2. In text mode, can use the same functions as with cin
    3. Can also open in binary mode and use read to read bytes
    4. The eof flag on an ifstream is not set until after a read fails, therefore the following code is incorrect

      ifstream mystream("hello.txt")
      int x = 0;
      while(mystream)
      {
          mystream >> x; // this read could result in the end of file
          print("X is {}", x)
      }
      
    5. The correct pattern is

      ifstream mystream("hello.txt")
      int x = 0;
      while(mystream >> x)
      {
          print("X is {}", x)
      }
      
      • The key point here is the read is in the loop condition, so if the read fails (e.g., due to reaching the end-of-file) the loop is not entered.
      • Also works with std::getline

Why

  1. Safety: only types that have operator>> implemented can be used without creating a compiler error
  2. Extensibility: it is possible to enable custom types to work with input streams and create custom stream manipulators for formatting options

C-Style

What

Using the input/output facilities that are available in the C standard library. These are here for completeness but should not be used in this class.

How

  1. #include<cstdio> (C++ standard library equivalent of <stdio.h>)
  2. For output: printf and fprintf
    • printf("The output is %d\n" 12)
    • printf takes a Format String that tells it how to format it's arguments
  3. For input scanf and fgets can be used
    • scanf has many dangerous pitfalls
    • fgets

Why

  1. Faster

Why Not?

  1. Dangerous: incorrect format strings can still compile and cause bugs
    • Modern compilers allow warning about the format strings and can catch many errors
  2. Dangerous: it is possible to get buffer overflows with scanf if proper precautions are not taken
    • Even most basic usages such as scanf("%s", mystring) can cause a buffer overflow.

Author: Matthew Elwin.