IO

Most C input is provided in a stream. That is, it is a series of characters made available one at a time. The scanf() function family (scanf/ fscanf/ sscanf) are format-sensitive functions; they not only collect the characters for you, but attempt to convert them to a type (such as an integer) that you specify.

They have great difficulty converting ZyGH4 to a meaningful number so they fail. The conversion attempt is governed by format specifiers that is provided. Since these may not match the input actually encountered, the family returns a value indicating the number of items successfully scanned and assigned. Examine the return:

If an error occurs it will not be automatically cleared. Operations on the stream will continue to return an error until clearerr(), fseek(), fsetpos() or rewind() is called. This means that a loop that is designed to pause for input will loop indefinitely.

The characters that f/s/scanf attempt to convert as one value are all the characters

The [ and c format directives are not whitespace delimited. If a character conflicts with the format specification, the function terminates and the character is left in the stream as if it had not been read. It will be the input for next input call, so it will not behave as expected.


Advantages of C++ stream I/O:
Predefined output operator for pointer types is also provided, allowing for the display of an object's address. By default, these values are displayed in hexadecimal notation.
#include <iostream>
using namespace std;

int main()
{
  int i = 1024;
  int* pi = &i;
  cout << "i:   " << i << "\t&i:\t" << &i << '\n';
  cout << "*pi: " << *pi << "\tpi:\t" << pi << endl
       << "\t\t&pi:\t" << &pi << endl;
  return 0;
}
Sample output
i:   1024         &i: 0x7ffff0b4
*pi: 1024         pi: 0x7ffff0b4
          &pi: 0x7ffff0b0
But for type const char*, it is not interpreted as address value but as C-style character string. To print out the address, override the default handling in two steps:
  1. first cast the const away
  2. then cast to type void*
const char *pstr const = "str";
cout << static_cast<void*>(const_cast<char*>(pstr));
Output of the built-in array type as well as the STL container require iteration through and output each individual element. Alternative way is using ostream_iterator.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

string pooh_pals[] = { "Tigger", "Piglet", "Eeyore", "Rabbit" };

int main()
{
  vector<string> ppals(pooh_pals, pooh_pals+4);
  vector<string>::iterator iter = ppals.begin();
  vector<string>::iterator iter_end = ppals.end();
  cout << "These are Pooh's pals: ";
  ostream_iterator<string> output(cout, " ");
  copy(iter, iter_end, output);
  cout << endl;

  return 0;
}
Its reversion is istream_iterator.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
  istream_iterator<string> in(cin), eos;
  vector<string> text;
  copy(in, eos, back_inserter(text));

  return 0;
}

Be aware of output operator has a higher precedence than the conditional operator ?:, therefore cout << (val1 > val2) ? val1 : val2; is evaluated as (cout << (val1 > val2)) ? val1 : val2;. The safer way is placed within parentheses cout << ((val1 > val2) ? val1 : val2);.


Stream variable returned by cin >> inputVariable or cin.getline function can be used to test successful of input instead of cin.fail(). If it is NULL/ fail but it is not end-of-file, it means didn't get input of the expected type/format.
while ((cin >> inputVariable) != NULL)
{
  // statements process inputVariable
}

if (!cin.eof())
{
  cerr << "format error" << endl;
  throw IOException;
}
After clear the error state can try to receive input with other data type.
double d;
while ((cin >> d) != NULL)
{
  // statements process d
}
if (!cin.eof())
{
  cin.clear(); // clear error state
  cin.ignore(); // clear buffer
  string s;
  cin >> s; // look for terminator string
  if (s != "end") {
    cerr << "format error" << endl;
    throw IOException;
  }
}
If the iostream is in an error state, input and output operations have no effect. Test it whether in a good state before subsequent call is important.
int i;
cin >> i;
string str;
while (str && (cin >> str) != NULL) // test str prevent forever loop
{
  // ...
}

Predefined set of manipulators is available for modifying the format state of stream object. A manipulator is applied to the stream object the same as if it were data. Rather than causing data to be read or written, however, manipulator modifies the internal state of the stream object. Applying the manipulator does not just change the representation of the value subsequently output. Rather, the internal format state of the stream object is modified.
To control how many chars are read in a string:
  1. Use the a format specifier in the scanf() function.
    char buf[25];
    scanf("%20s", buf); /* read to 20 characters maximum */
    
  2. For C++ use either setw() or cin.get() or cin.getline().
    // read (sizeof buf) - 1 characters max and put \0 for the
    // (sizeof buf)-th character
    cin >> setw(sizeof buf) >> buf;
    // or
    cin.get(buf, sizeof buf);
    // or
    cin.getline(buf, sizeof buf);
    
get() (istream& get(char& ch), int get(void) and istream& get(char* sink, streamsize size, char delimiter='\n')) or istream& getline(char* sink, streamsize size, char delimiter='\n') both read the input stream as a sequence of uninterpreted bytes rather than as a sequence of data types. To use get() need to remember to discard the delimiter using ignore(streamsize length=1, int delim=traits::eof) before reapplying it; getline() is prefered because it discards the delimiter rather than leaving it as the next istream character. gcount() returns number of character actually read.
const int STR_SIZE = 1024;
char str[1024];
while (cin.get(str, STR_SIZE))
{
  // ...  
  if (cin.gcount() & STR_SIZE-1)
    cin.ignore(); // discard newline if encounter before read next line
}
C++ standard library also provides a non-member getline() instance that inputs into a string object. A maximum of str::max_size()-1 characters are read. If the input sequence exceeds this limit, the read operation fails and the istream object is placed in an error state. Otherwise, input ceases when either the delimiter is read (it is discarded from the istream, but not inserted into the string) or end-of-file is encountered.
#include <iostream>
#include <string>
using namespace std;

int main()
{
    string name;
    cout << "Enter a name:\n";
    getline(cin, name);
    cout << name << endl;

    return 0;
}

getline() does not work correctly in Visual C++ 6.0 (need to hit twice for the program to read your input) because there's a known bug in Visual C++'s implementation.


Code fragment illustrates putback(char) (push character back into the iostream), unget(void)(resets pointer to 'next' istream item backward by one) and peek(void)(returns next character or EOF but does not extract it):
const lineSize = 1024;
char ch, next, lookahead;
while (cin.get(ch))
{
  switch (ch) {
    case '/': // check line comment
      next = cin.peek();
      if (next == '/')
        cin.ignore(lineSize, '\n'); // ignore rest of line
      break;
    case '>':
      // look for >>=
      next = cin.peek();
      if (next == '>')
      {
        lookahead = cin.get();
        next = cin.peek();
        if (next != '=')
          cin.putback(lookahead);
      }
      // ...
  }
}

Unix/Linux have only one mode for file operations: binary. Windows has a text mode which is, unfortunately, the default. In text mode, content beyond what you supply is written to the file; it is stripped when you read the file. This makes the use of random access (seek, tell, etc.), problematic.

Even worse, a 'soft' EOF, a special character, is written. This indicator is not moved if you do an append, so appended data is not reachable unless you reset the EOF indication. Consequently, always using binary mode. With fopen, specify a read or write or other mode as "rb", "wb", etc. Unix/Linux will accept and ignore the 'b', so you can use it portably. Can set the default mode in Windows with the global variable, _fmode.


Use ifstream object for read-only file, ofstream object for output-only and fstream object for read/write file. The ofstream is derived from the ostream. All the ostream operations can be applied to an ofstream class object. Similarly, ifstream is derived from the istream, fstream is derived from iostream, iostream is derived from istream and ostream. Finally istream and ostream is derived from ios virtually.

ofstream file can be opened using its constructor or its open() member function in either output (ios_base::out, by default) or append (ios_base::app) mode. Before attempting to read or write to a file, verify that it has been opened successfully. It will be closed automatically when destructor invoked or using close() member function.
string fileName;
cout << "file name: ";
cin >> fileName;
ofstream outfile(fileName.c_str(), ios_base::append);
if (!outFile) { // opened failed
  cerr << "cannot open " << fileName << " for output" << endl;
  exit(-1);
}
fstream file can be repositioned using either seekg() or seekp(). (g indicates positioning for getting characters whereas p indicates positioning for putting characters). seekg(pos_type current_position) set a fixed position within a file; seekg(off_type offset_position, ios_base::seekdir dir) offset some amount from current position in some direction. dir can be set to one of the following:

The current read position in a file is returned by either tellg() or tellp()

					
// positions the file at the ith record entry for each iteration
for (int i = 0; i<recordCnt; i++)
  readFile.seekg(i * sizeof(Record), ios_base::beg);

// advance one Record from the current file position
readFile.seekg(readFile.tellg() + sizeof(Record));
// or more efficient,
readFile.seekg(sizeof(Record), ios_base::cur);

// mark current position
ios_base::pos_type mark = writeFile.tellp();
// ...
if (cancelEntry)
  writeFile.seekp(mark); // return to marked position

Given a text file to read, compute the byte size of the file and store it at the end of the file. In addition, each time encounter a newline character, store the current byte size, including the newline, at the end of the file. For example, given the following text file,
abcd
efg
hi
j
Output
abcd
efg
hi
j
5 9 12 14 [ 24 ]
Implementation
string fileName;
cout << "file name: ";
cin >> fileName;
// open in both input and append mode
fstream inOut(fileName.c_str(), ios_base::in|ios_base::app);
if (!inOut)
{
  inOut.seekg(0); // file position is at its end when opened in append mode

  int cnt = 0; // byte count
  char ch;
  while (inOut.get(ch))
  {
    cout.put(ch); // echo on terminal for debug
    cnt++;
    if (ch == '\n')
    {
      // need mark current position as it repositioned at its end once write
      ios_base::pos_type mark = inOut.tellg();
      inOut << cnt << ' ';
      inOut.seekg(mark); // restore position
    }
  }

  inOut.clear(); // need to clear EOF state so can write again
  // write out final byte count
  inOut << cnt << endl;
  cout << "[ " << cnt << " ]" << endl;
}

Nested formatting output:
class Class1 {
public:
  friend ostream& operator<<(ostream& outStream, const Class1& c) {
    outStream << vsl_indent() << "Class1:" << endl;
    vsl_indent_inc(outStream);
    outStream << vsl_indent() << "Class1’s data" << endl;
    vsl_indent_dec(outStream);
    return outStream;
  }
};

class Class2 {
public:
  friend ostream& operator<<(ostream& outStream, const Class2& c) {
    outStream << vsl_indent() << "Class2:" << endl;
    vsl_indent_inc(outStream);
    outStream << vsl_indent() << c.class1_ << endl;
    outStream << vsl_indent() << "Class2’s other data" << endl;
    vsl_indent_dec(outStream);
    return outStream;
  }

protected:
  Class1 class1_;
};

// usage in application
Class2 class2;
cout << class2 << endl;
Output
Class2:
  Class1:
    Class1’s data
  Class2’s other data

Using string stream
#include <string>
#include <fstream>
#include <sstream>
using namespace std;

string ReadTextFile(string fileName)
{
  ifstream ifile(fileName);
  ostringstream buf;
  char ch;
  while (buf && ifile.get(ch))
    buf.put(ch);
  return buf.str();
}
Index