mirror of
https://gitlab.com/fbb-git/cppannotations
synced 2024-11-16 07:48:44 +01:00
d84a4013af
git-svn-id: https://cppannotations.svn.sourceforge.net/svnroot/cppannotations/trunk@485 f6dd340e-d3f9-0310-b409-bdd246841980
135 lines
7 KiB
Text
135 lines
7 KiB
Text
In order to both read and write to a stream
|
|
hi(stream: read and write) an hi(fstream)tt(std::fstream) object must be
|
|
created. As with tt(ifstream) and tt(ofstream) objects, its constructor
|
|
receives the name of the file to be opened:
|
|
centt(fstream inout("iofile", ios::in | ios::out);)
|
|
Note the use of the constants hi(in)tt(ios::in) and hi(out)tt(ios::out),
|
|
indicating that the file must be opened for both reading and writing. Multiple
|
|
mode indicators may be used, concatenated by the tt(bitor) operator.
|
|
Alternatively, instead of tt(ios::out), hi(app)tt(ios::app) could have been
|
|
used and mere writing would become appending (at the end of the file).
|
|
|
|
Reading and writing to the same file is always a bit awkward: what to do
|
|
when the file may not yet exist, but if it already exists it should not
|
|
be rewritten? Having fought with this problem for some time I now use
|
|
the following approach:
|
|
verbinclude(examples/existingreadwrite.cc)
|
|
Under this approach if the first construction attempt fails tt(fname)
|
|
doesn't exist yet. But then tt(open) can be attempted using the
|
|
hi(trunc)tt(ios::trunc) flag. If the file already existed, the construction
|
|
would have succeeded. By specifying hi(ate)tt(ios::ate) when defining tt(rw),
|
|
the initial read/write action would by default have taken place at
|
|
endOfFile().
|
|
|
|
Under hi(MS-WINDOWS) hi(DOS) bf(DOS)-like operating systems that use the
|
|
multiple character sequence tt(\r\n) to separate lines in i(text files) the
|
|
flag hi(binary)tt(ios::binary) is required to process i(binary file)s ensuring
|
|
that tt(\r\n) combinations are processed as two characters. In general,
|
|
tt(ios::binary) should be specified when binary (non-text) files are to be
|
|
processed. By default files are opened as text files. i(Unix) operating
|
|
systems do not distinguish text files from binary files.
|
|
|
|
With tt(fstream) objects, combinations of file flags are used to make sure
|
|
that a stream is or is not (re)created empty when opened. See section
|
|
ref(OUTPUTMODES) for details.
|
|
|
|
Once a file has been opened in read and write mode, the lshift() operator
|
|
can be used to insert information into the file, while the rshift() operator
|
|
may be used to extract information from the file. These operations may be
|
|
performed in any order, but a tt(seekg) or tt(seekp) operation is required
|
|
when switching between insertions and extractions. The seek operation is used
|
|
to activate the stream's data used for reading or those used for writing (and
|
|
em(vice versa)). The tt(istream) and tt(ostream) parts of tt(fstream) objects
|
|
share the stream's data buffer and by performing the seek operation the stream
|
|
either activates its tt(istream) or its tt(ostream) part. If the seek is
|
|
omitted, reading after writing and writing after reading simply fails. The
|
|
example shows a white space delimited word being read from a file, writing
|
|
another string to the file, just beyond the point where the just read word
|
|
terminated. Finally yet another string is read which is found just beyond the
|
|
location where the just written strings ended:
|
|
verb(
|
|
fstream f("filename", ios::in | ios::out);
|
|
string str;
|
|
|
|
f >> str; // read the first word
|
|
|
|
// write a well known text
|
|
f.seekg(0, ios::cur);
|
|
f << "hello world";
|
|
|
|
f.seekp(0, ios::cur);
|
|
f >> str; // and read again
|
|
)
|
|
Since a em(seek) or em(clear) operation is required when alternating
|
|
between read and write (extraction and insertion) operations on the same file
|
|
it is not possible to execute a series of lshift() and rshift() operations in
|
|
one expression statement.
|
|
|
|
Of course, random insertions and extractions are hardly ever used. Generally,
|
|
insertions and extractions occur at well-known locations in a file. In those
|
|
cases, the position where insertions or extractions are required can be
|
|
controlled and monitored by the tt(seekg), tt(seekp, tellg) and tt(tellp)
|
|
members (see sections ref(OSTREAMPOS) and ref(ISTREAMPOS)).
|
|
|
|
Error conditions (see section ref(IOSTATES)) occurring due to, e.g., reading
|
|
beyond end of file, reaching end of file, or positioning before begin of file,
|
|
can be cleared by the tt(clear) member function. Following tt(clear)
|
|
processing may continue. E.g.,
|
|
verb(
|
|
fstream f("filename", ios::in | ios::out);
|
|
string str;
|
|
|
|
f.seekg(-10); // this fails, but...
|
|
f.clear(); // processing f continues
|
|
|
|
f >> str; // read the first word
|
|
)
|
|
A situation where files are both read and written is seen in
|
|
em(database) applications, using files consisting of records having fixed
|
|
sizes, and where locations and sizes of pieces of information are known. For
|
|
example, the following program adds text lines to a (possibly existing)
|
|
file. It can also be used to retrieve a particular line, given its
|
|
order-number in the file. A emi(binary file) tt(index) allows for the quick
|
|
retrieval of the location of lines.
|
|
verbinclude(examples/readwrite.cc)
|
|
Another example showing reading em(and) writing of files is provided by
|
|
the next program. It also illustrates the processing of i(ASCII-Z) delimited
|
|
strings:
|
|
verbinclude(examples/asciiz.cc)
|
|
|
|
A completely different way to read and write streams may be implemented
|
|
using tt(streambuf) members. All considerations mentioned so far remain valid
|
|
(e.g., before a read operation following a write operation tt(seekg) must be
|
|
used). When tt(streambuf) objects are used, either an
|
|
tt(istream) is associated with the tt(streambuf) object of another tt(ostream)
|
|
object, or an tt(ostream) object is associated with the
|
|
tt(streambuf) object of another tt(istream) object. Here is the previous
|
|
program again, now using
|
|
hi(streams: associating) em(associated streams):
|
|
verbinclude(examples/readwrite2.cc)
|
|
In this example
|
|
itemization(
|
|
it() the streams associated with the tt(streambuf) objects of existing
|
|
streams are not ti(ifstream) or ti(ofstream) objects but basic ti(istream) and
|
|
ti(ostream) objects.
|
|
it() The tt(streambuf) object is not defined by an tt(ifstream) or
|
|
tt(ofstream) object. Instead it is defined outside of the streams, using
|
|
a tt(filebuf) (cf. section ref(FILEBUF)) and constructions like:
|
|
verb(
|
|
filebuf fb("index", ios::in | ios::out | ios::trunc);
|
|
istream index_in(&fb);
|
|
ostream index_out(&fb);
|
|
)
|
|
it() An tt(ifstream) object can be constructed using stream modes normally
|
|
used with tt(ofstream) objects. Conversely, an tt(ofstream) objects can be
|
|
constructed using stream modes normally used with tt(ifstream) objects.
|
|
it() If tt(istream) and tt(ostreams) share a tt(streambuf), then their
|
|
read and write pointers (should) point to the shared buffer: they are tightly
|
|
coupled.
|
|
it() The advantage of using an external (separate) tt(streambuf) over a
|
|
predefined tt(fstream) object is (of course) that it opens the possibility of
|
|
using tt(stream) objects with specialized tt(streambuf) objects. These
|
|
tt(streambuf) objects may specifically be constructed to control and interface
|
|
particular devices. Elaborating this (see also section ref(STREAMBUF)) is left
|
|
as an exercise to the reader.
|
|
)
|