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.
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
[
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.
int i = 5; float f = 3.4; printf("%d %f\n", f, i); /* few compilers catch this! */ cout << f << " " << i << endl; // automatically right
#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: 0x7ffff0b0But 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:
const
away
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);
.
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 { // ... }
false
literal value is output
as 0, true
as 1. Override the default by
applying the boolalpha
manipulator; use
noboolalpha
to unset the modification.
cout << "default bool values: " << true << " " << false << "\nalpha bool values: " << boolalpha << true << " " << false << endl;
hex
, oct
and
dec
. showbase
causes an integral
value's output to indicate its base by leading Ox and O
for hexadecimal (apply the uppercase
manipulator to display the X in upper-case,
nouppercase
for lower-case) and octal.
iomanip
function setbase(int base)
also can output integer in desired base.
int ival = 16; double dval = 16.0; cout << showbase; << "ival: " << ival << " oct set: " << oct << ival << "\n" << "dval: " << dval << " hex set: " << hex << dval << "\n" << "ival: " << ival << " dec set: " << dec << ival << "\n" << noshowbase;
int precision(int)
member function or
iomanip
functions setprecision()
and setw()
. Floating point values are rounded,
not truncated.
#include <iostream> #include <iomanip> using namespace std; int main(void) { float f = 105.2345; char buf[] = "This is a string"; cout.setf(ios::fixed); // Limit to 2 decimals int old_precision = cout.precision(); // or cout << setprecision(2) << f << endl; // Print 10 chars wide, limit to 2 decimals cout << setw(10) << setprecision(2) << f << endl; cout << setw(30) << buf << endl; // right justify to 30 chars cout.precision(old_precision); return 0; }
left
; right
resets it to default.
internal
left justifies the sign and right
justifies the value, padding the intervening space with
blanks. If wish to fill the blank spaces with alternative
character, apply the iomanip
functions
setfill(char)
.
showpoint
/noshowpoint
.
To change the display to scientific notation, use the
scientific
manipulator (apply the
uppercase
manipulator to display the E in
upper-case, nouppercase
for lower-case); to
change it back to fixed decimal, use fixed
.
cout << showpoint << 10.0 << noshowpoint << '\n' << "scientific: " << scientific << 10.0 << "fixed decimal: " << fixed << 10.0 << '\n';
noskipws
causes the input operator not to skip
white space; to return to the default behavior, use
skipws
. ws
"eat" white space.
flush
, ends
or endl
.
unitbuf
, an internal stream state variable,
can be set to empty the buffer after each output operation.
The ostream object can be tied to an istream, in which case
the ostream buffer is flushed whenever the istream reads
from the input stream. cout
is predefined to
be tied to cin
. So input statement after
output statement causes the output buffer to be flushed.
An ostream object can be tied to only one istream object
at a time. To break an existing tie, pass in argument of 0.
istream is; ostream new_os; char ch; // tie() returns existing tie ostream* old_tie = is.tie(); is.tie(0); // break existing tie is.tie(&new_os); // set new tie new_os << "Press a key and ENTER to flush: \n"; is >> ch; // cause new_os flush is.tie(0); // break existing tie is.tie(old_tie); // reestablish old tie
scanf()
function.
char buf[25]; scanf("%20s", buf); /* read to 20 characters maximum */
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
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
abcd efg hi jOutput
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; }
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
#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