Stream concept A stream represent a sequence of bytes arriving, being retrieved, being stored, or being sent, in order. A stream is continuos and offer sequential access to the data. Each byte can be read from the stream only in the order arriving or stored, and it is not possible to read something twice, or go back in the stream to read previous bytes. The order it fixed. The data can have two directions. It can come in to the program from the stream, or come out from the program to the stream. A stream acting as a source for data (coming in to the program) is called an in-stream. If it act as destination for data (going out from the program) it is an out-stream. Only special streams can also offer random access, where you can go back and read bytes again. Most streams are text-streams, where every byte is considered one single character, and data is stored in human readable form, as characters. Streams in C++ All streams in C++ have a specific data-type. The data-type determine what operations that can be done on the specific stream. In addition all streams in C++ have a common data-type. It provide operations that are common for all streams. By using the common data-type instead of a specific it is possible to easily interchange one stream for another. For example in arguments to a function, where sometimes we want to read data from a file, and sometimes we want the same data from the keyboard. When you create a stream you must create the specific stream type you want. To get access to that type you must include the corresponding header file. cin, cout and cerr are predefined C++ stream variables that you already used. Input and output to other stream variables are written just the same. Only the stream variable will be different, causing the data source or destination to be different (for cin the source of data is the keyboard, for an input file stream the source would be a file). Reference information Stream type declarations UNIX terminal commands to find reference information: man ios man istream man ostream man basic_string man stringstream man fstream man ctype man... (do you get the pattern?) man numeric_limits The man-pages are sometimes hard to read and understand for the beginner programmer. It is also very difficult to know what to look for. The man-pages will however be available on the exam - so it is may be worthwhile to learn how to use them. If you do not know what to look up, look up something similar, or a good guess, and use the see also section to get further ideas. You can also use google to get ideas. Online reference information (not available on exam): http://www.cplusplus.com/reference/iostream/ Common stream types: #include <iostream> // declarations for reference to any stream // in this case references to cin and cout istream& cin_alias = cin; // or other stream ostream& cout_alias = cout; Standard streams: #include <iostream> // the following variables are defined by // default, you do not need to define them cin // for normal input (keyboard) cout // for normal output (screen) cerr // for error messages (screen) String streams (must be connected to string variable): #include <sstream> // create a in-string-stream variable iss istringstream iss; // create a out-file-stream variable oss ostringstream oss; File streams (must be connected by opening some file): #include <fstream> // create a in-file-stream variable ifs ifstream ifs; // create a out-file-stream variable ofs ofstream ofs;
Stream operations (1 of 2) Stream operations (2 of 2) Functions available on all streams (member functions): bool bad(); // check different error flags bool eof(); bool fail(); // most useful, operator! bool good(); void clear(); // clear error flags (ONLY) Functions available on istreams (member functions): istream& operator>> // formatted input istream& get(char& c); // istream& getline(char* s, streamsize n, // char delim); // int sync(); istream& ignore(streamsize n, int delim); istream& read(char* s, streamsize n); streampos tellg(); istream& seekg(streampos pos); Complementary free function (part of string library) istream& getline(istream&, string&, char); Functions available on ostreams (member functions): ostream& operator>> // formatted output ostream& put(char c); ostream& flush(); streampos tellp(); ostream& seekp(streampos pos); ostream& write(const char* s, streamsize n); Manage connections on stringstreams (member functions): void str(const string& s); string str(); Manage connections on fstreams (member functions): void open(const char* filename, ios_base::openmode mode); void close(); Open modes: ios::app ios::ate ios::binary ios::ate ios::in ios::out ios::trunc The following operations from previous page is commonly used only on file streams: streampos tellg(); istream& seekg(streampos pos); streampos tellp(); ostream& seekp(streampos pos, ios_base::seekdir); Seek dirs: ios::beg ios::cur ios::end The following operations from previous page is commonly used only on binary file streams: istream& read(char* s, streamsize n); ostream& write(const char* s, streamsize n); Stream use, result and chaining In many of the following examples we will use cin. Remember that cin in the examples can be replaced with any stream type variable. Result of stream operations and chaining: // the result of stream operations // is the stream itself // this allows chaining the operations cin >> foo >> bar >> fum; (((cin >> foo) >> bar) >> fum); // >--istream-< // >------istream------< //>-----------istream----------< A stream can occur where C++ expects a boolean value. The stream is converted to bool as if not fail was called. if ( cin ) // if (! cin.fail() ) // stream is in working order (good) if (! cin ) // if ( cin.fail() ) // stream will not work, an error is set // will silently fail to do ANY input // until the error is handled Streams as parameters and return values Streams can be sent as parameters to functions. It allows you to do a generic function to read or write your data, and decide which stream to use later, when you call the function. N.B! Streams must always be references! void print_table(ostream& os) for (int i = 0; i < 10; i = i + 1) os << setw(4) << i << endl; ostream& print_it(ostream& os) os << It ; return os; ofstream file( my_file.txt ); print_table(file); print_table(cerr); print_it(cout) << works. << endl;
Examples: To end-of-file Examples: Fail-safe reading string line; // read integers until failure // THIS IS CORRECT while (cin >> foo) cout << foo; // do something with foo Common pitfall: // read integers until eof // WRONG WRONG ERROR WRONG BAD AVOID // DO NOT DO LIKE THIS while (! cin.eof() ) cin >> foo; // read something to foo cout << foo; // do something with foo // read integers until eof // CORRECTED BUT AVKWARD VERSION while (! cin.eof() ) cin >> foo; // read something to foo if (! cin.eof() ) cout << foo; // do something with foo // read an integer between 1-10 fail-safe cin >> foo; // read something to foo // test if everything was OK immediately if (! cin.fail() ) if (foo >= 1 && foo <= 10) // success, data valid else // good read, but invalid data // if it was not OK, find the specific error // always test worst case first since the // worst errors may trigger a less bad error else if (cin.bad()) // failed, unrecoverable else if (cin.eof()) // end of file, end of stream // can be reset with clear if the // stream is standard input (cin) else // fail // interpretation to data-type failed // you must acknowledge the error cin.clear(); // remove the offending data from the // stream by a call to sync, ignore, get // getline, or >> to another data-type cin.ignore(1024, \n ); Removing offending data Examples: Line by line string line; string field; // simple solution that allows your program // to take look at the bad data cin >> word; // as above, but a full line getline(cin, line); getline(cin, line, \n ); // read one field (separated with : ) getline(cin, field, : ); // ignore at most 1000 characters or // at most one full line (until newline) // (whichever comes first) cin.ignore(1000, \n ); // read lines until failure while (getline(cin, line)) cout << line; // do something... // read?what? until failure while (getline(cin >> foo, line)) cout << line << : << foo; // read one full line // then read the data on the line while (getline(cin, line)) istringstream iss; // or: iss(line) int count = 0; // ignore one full line cin.ignore(numeric_limits<streamsize>::max(), \n ); // ignore as much as possible until ; cin.ignore(numeric_limits<streamsize>::max(), ; ); // just like ignore to end of line while (cin.get(c) && c!= \n ) ; // not guaranteed to work in all cases // (we have experienced different behaviour // on different systems, use ignore instead) cin.sync(); // tell iss to read from the line variable iss.str(line); while (iss >> foo) // pitfall: early eof() count = count + 1; // observe how to format long cout lines cout << line contained << count << integers. << endl;
Examples: Type conversion Examples: Read a file (1 of 3) double string_to_double(const string& s) istringstream text_representation(s); double floating_point; text_representation >> floating_point; return floating_point; string double_to_string(double d) ostringstream text_representation; text_representation << d; return text_representation.str(); // read file (hardcoded litteral filename) ifstream infile; // open file for reading... infile.open( my_data.txt ); if (! infile) // read data... while (infile >> foo) cout << foo << endl; // close filestream as soon as input and // output to it is done! Examples: Read a file (2 of 3) Examples: Read a file (3 of 3) // read file (program ask for filename) ifstream infile; string my_file; cout << enter file name: ; cin >> my_file; // open file for reading... // N.B! you must use c_str() to get the // correct data-type from the string infile.open(my_file.c_str()); if (! infile) // read data... while (infile >> foo) cout << foo << endl; // read file (filename from command line) int main(int argc, char* argv[]) string my_file; if (argc < 2) cerr << Usage: << argv[0] << FILENAME << endl; // open and clear file... // you can declare and open at once ifstream infile(argv[1], ios::trunc); if (! infile) // read data... while (infile >> foo) cout << foo << endl;
Examples: Write a file Writing is essentially identical to reading. You can modify this in the same way as the previous examples reading a file (exercise). ofstream outfile; // open file for reading... outfile.open( my_data.txt ); if (! outfile) // write data... while (cin >> foo) outfile << foo << endl; Binary streams Some streams can be configures to be binary, where data can be read or written without conversion to text. It is then written exactly as stored in the computer memory by the specific computer type you are working on. Different computers store binary data in different ways. For example our thin client servers running Solaris use SPARC processors that store binary integers in bigendian form, whereas a PC running Windows use the x86 architecture using little-endian form. You can compare to how dates are written in different parts of the world. Some countries write the smallest unit first, like DD-MM-YY, whereas other write the larger unit first, like YY-MM-DD. When you write data in one format you must also take care to read it in the same form. This poses a problem when you transfer binary files between different computer platforms, or over the network. outfile.close(); For example, if you save a binary file for lab 3 at home ( little-endian ), and then read it again on our thin clients it will not work, unless you take actions to convert the read bytes to big-endian form. Endianness or byte-order Example: Binary files (1 of 2) #include <cmath> little endian big endian Address Memory 0x03 bit 31... bit 24 0x02 bit 23... bit 16 0x01 bit 15... bit 8 0x00 bit 7... bit 0 Address Memory 0x03 bit 7... bit 0 0x02 bit 15... bit 8 0x01 bit 23... bit 16 0x00 bit 31... bit 24 double pi_out = M_PI; // 3.1415... double pi_in = 0.0; cout << "out: " << pi_out << endl; ofstream outfile("pi.bin", ios::binary); if (! outfile) cerr << Error opening file << endl; outfile.write( reinterpret_cast<char*>(&pi_out), sizeof(pi_out) ); outfile.close(); ifstream infile("pi.bin", ios::binary); if (! infile) cerr << Error opening file << endl; infile.read( reinterpret_cast<char*>(&pi_in), sizeof(pi_in) ); cout << "in: " << pi_in << endl;
Example: Binary files (2 of 2) Writing strings (data-type string) binary is not suitable, as the representation in memory of a string contain addresses that are specific to the particular program execution, and will be invalid the next time the program is run. Thus it is not possible to write a string binary, and expect the same string when you read it back. You will get an error that crash your program. The same apply to many types of data. The basic types long, int, double, float, char is safe to write binary. Use the normal formatted output operator to write a string. To write a fix-length string you can use setw as usual. const int SIZE = 9; string s = long example data ; os << setw(size) << s.substr(0, SIZE); // writes long exam to stream const int SIZE = 9; string s = mini ; os << setw(size) << s.substr(0, SIZE); // writes mini to stream