fstream ?

J

JKop

Before I resort to the "fopen" family, can some-one please direct me to a
tutorial on the net about the "fstream" family.

I've gone through about 10 references and they're all complete bullshit. I'm
trying to find simple answers to simple questions.

What exactly does the constructor do? How does the whole eof thing work?


-JKop
 
N

Nicolas Pavlidis

JKop said:
Before I resort to the "fopen" family, can some-one please direct me to a
tutorial on the net about the "fstream" family.

Not a tutorial but a reference, maybe it helps:
http://www.cppreference.com/cppio.html
I've gone through about 10 references and they're all complete bullshit. I'm
trying to find simple answers to simple questions.

What exactly does the constructor do? How does the whole eof thing work?

See the reference.

HTH && Kind regrads,
Nicolas
 
F

Fraser Ross

The eof flag is set after an attempt to read past the end. It won't be set
after opening a file of size 0.

Fraser.


"JKop"
 
J

John Harrison

Fraser Ross said:
The eof flag is set after an attempt to read past the end. It won't be set
after opening a file of size 0.

Fraser.

int i;
file >> i;

This may successfully read an integer AND set the end of file. If the
integer is the very last thing in the file then the end of file will be set
because you have to read past the end of file to determine that the integer
has been fully read. But if there is a space after the last integer then
reading that space will tell you that the integer has been fully read and
end of file will not be set.

Same applies to getline and other similar read functions that have to read
to some terminator.

John
 
K

Karl Heinz Buchegger

JKop said:
Before I resort to the "fopen" family, can some-one please direct me to a
tutorial on the net about the "fstream" family.

I've gone through about 10 references and they're all complete bullshit. I'm
trying to find simple answers to simple questions.

And the simple answers are:
The 'fstream' family works exactly like the 2 objects you already are
familiar with: cin, cout

The only difference is: cin and cout are predefined objects, while you
have to create the file-stream objects on your own.
What exactly does the constructor do?

Creating the stream object. Depending on which constructor you use exactly,
the constructor may also open the file and connect the stream with it. The
stream is then immediatly ready to be read from or write to.

How does the whole eof thing work?

eof() works different in C or C++, then it does in a lot of other
languages. In C or C++ the runtime system does not try to tell the
future. As a consequence, eof returns true only if you try *and* failed
to read past the end of the stream.
So eof() is typically used to figure out, why a previous read operation
has failed. If it was because eof() then everything is ok, the file
could have been read correctly till the end.

As an example, study this code snippet, which
reads a text file line by line and copies the
read line into a new file (just to do something
with the data)

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main()
{
ifstream InFile( "C:\\test.dat" ); // ifstream: input file stream
ofstream OutFile( "C:\\Copy.dat" ); // ofstream: output file stream

if( !InFile.is_open() ) {
cout << "Could not open input file" << endl;
return EXIT_FAILURE;
}

if( !OutFile.is_open() ) {
cout << "Could not open output file" << endl;
return EXIT_FAILURE;
}

string InLine;

while( getline( InFile, InLine ) )
OutFile << InLine;

if( !InFile.eof() )
cout << "Error during read from file" << endl;
}

Note also the typical read loop:

while( data_can_be_read ) {
process data
}

// figure out why the loop terminated.
// if it was because eof, everything is fine
if( !eof() )
Process error during read
 
J

JKop

Thanks Karl.

Here's the gist of what I'm trying to do at the moment: Let's say I have a
file "mammals.txt" and it contains:

cat
dog
ape
monkey
horse
pig

What I want to do is remove a certain line, let's say "horse", so that after
the program runs, "mammals.txt" will look like so:

cat
dog
ape
monkey
pig

Here's what I'm doing so far:

std::ifstream mammal_file( "mammal.txt" );

if ( !mammal_file.is_open() )
{
std::cout << "ERROR: Unable to open \"mammal.txt\"\n";
throw AnException();
}

//The file is now open
std::string one_line;


What I'm intending to do is copy all the lines into a new *output* file
stream, except "horse", and then overwrite the original. Would you be able
to provide any tips at all please?


-JKop
 
K

Karl Heinz Buchegger

JKop said:
What I'm intending to do is copy all the lines into a new *output* file
stream, except "horse", and then overwrite the original. Would you be able
to provide any tips at all please?

Sure.

You are on the right track. When dealing with text files (except in some
rare circumstances), then the only possible way to modify that file is
be writing a new one and while writing that file apply all the intended
modifications. The only exception is when the replacement takes up the
same amount of bytes as the original, then you can simply seek to the
position and overwrite. But those cases are rare.

The next thing you should think about is:
How do I read. I mean: Which functionality do I use.
In your case you have 2 choices

you could eg.

std::string InWord;
InFile >> InWord;

This will work in your case, since each line contains only a single word.
So the basic loop would look like this

...
while( mammal_file >> InWord ) {
if( InWord != "horse" )
OutFile << InWord << "\n";
}
...

But I often feel that a different approach is simpler. What is
the problem? The problem comes if the file format gets more complicated,
saying you have 3 words on each line and there have to be exactly 3 words
on each line or otherwise the record (the line) is in error. So how to do this?
In cases like this it is often simpler to use getline (as I did in the
previous post). You use getline to read in the whole line as a single string
and then use eg. string functions to seperate that total string into the
words. Or you could use a stringstream to do the same.
So why is that often simpler. Think of the error case. By using getline
you have read the whole record, so if you figure out that that record
is bogous you don't need to do anything special with the input stream. You
just don't do anything with the bogus record and continue reading (and
emit an error message).
But in the read loop using >>?
Well, first of all you need to detect that the line is in error. >> ignores
line breaks because they are considered as white space. So detecting that
the number of words per line is wrong isn't that simple. Even then, lets say
the file format asks for a number, but in the file there is a text string "abc".need to read until the end of that record is read (the line break) for finding
the start of the next record. With the getline-version this is done all automatically.

But there is one (minor) point with getline. getline reads *and* includes the terminator
(in this case '\n'). So often one needs to get rid of that terminator are at least
deal with it. In your specific case, that would eg. be

while( getline( mammal_file, one_line ) ) {
if( one_line != "horse\n" )
OutFile << one_line;
}

Note: In the version using >> I had to write a '\n' on my own to the output stream, becausehas placed a '\n' in one_line as last character. That is: if there was one in the file.
The last line in the file may or may not contain a terminating '\n'.

So much for the thoughts about how to get at the file content.
Back to your program.
Open the output file and write to it whatever loop you want to choose.
Once you have finished, all that is left is

* delete the original file
you will want to use function remove() from <cstdio> for that
* rename the newly created file to the original file name
you will want to use function rename() from <cstdio> for that

It is a good idea to actually close the streams before the deletion and
renaming takes place.

As for: How to name that temporary file.
Often just a variation of the input file name is used. If the file system
supports file extensions, then it is often the name of the input file with
a different extension. But there is a second solution. As a leftover from
C there is a function called tmpnam, which generates a filename to be guaranteed
to not collide with any other file name in the directory. But look up the documentation
for that function. Often there are some tricky interactions with environment variables.
 
J

JKop

Here's how it's going so far:

#include <fstream>
#include <string>

int main()
{
std::ifstream mammals_file( "mammals.txt" );

if ( !mammals_file.is_open() )
{
std::cout << "ERROR: Unable to open \"mammals.txt\"\n";
return -1;
}

//The file is now open

{
std::eek:fstream out_file;

std::string one_line;

do
{
std::getline(mammals_file, one_line);

if ( !CompareWithoutRegardToCaseX( "horse", one_line.c_str() )
) out_file << one_line << '\n'; //Should I have that '\n' there?
}
while ( !mammals_file.eof() );

//Now save the file, overwriting the original, but how . . . ?
}
}


Any thoughts?


-JKop
 
J

JKop

Thanks.

I'd written a reply and then two seconds later I wrote another
* delete the original file
you will want to use function remove() from <cstdio> for that
* rename the newly created file to the original file name
you will want to use function rename() from <cstdio> for that

I was wondering if I'd have to name the file in the first place? Is there
anyway to just have it reside in memory, and then at my discretion save it
as a file?

One thing also, I'll have to use "getline" because there could be spaces on
the lines in these files.


-JKop
 
K

Karl Heinz Buchegger

JKop said:
Here's how it's going so far:

#include <fstream>
#include <string>

int main()
{
std::ifstream mammals_file( "mammals.txt" );

if ( !mammals_file.is_open() )
{
std::cout << "ERROR: Unable to open \"mammals.txt\"\n";
return -1;
}

//The file is now open

{
std::eek:fstream out_file;

You want to pass a file name to the ofstream constructor, such that it
opens the file for you.
std::string one_line;

do
{
std::getline(mammals_file, one_line);

if ( !CompareWithoutRegardToCaseX( "horse", one_line.c_str() )
) out_file << one_line << '\n'; //Should I have that '\n' there?

You used getline. So getline will include the terminator '\n' in the read
string. There is however one potential pitfall, the last line in the file
may not be terminated with '\n', thus the string will not contain it.

Best is often: read with getline, and look at the last character in the string.
If it is a '\n', discard it.

}
while ( !mammals_file.eof() );

//Now save the file, overwriting the original, but how . . . ?

delete original file with remove()
rename written file to original file name using rename()


out_file << one_line
 
K

Karl Heinz Buchegger

JKop said:
Thanks.

I'd written a reply and then two seconds later I wrote another


I was wondering if I'd have to name the file in the first place? Is there
anyway to just have it reside in memory, and then at my discretion save it
as a file?

std::vector<std::string> will happily store all the strings until
you are ready to

* close the original file
* open the same file for output (which will automatically truncate it
to 0 size)
* write the vector content to it

But note: In a high secure environment that would not be acceptable.
During the time period of opening the file for writing and the actual
finishing of writing, the data exist in memory only. If the computer
crashes during that time (eg. power outage) the data is lost forever.

Using the other version of writing to a temporary and then deleting/renaming
ensures that there is always one complete copy of the whole data set on
the hard disk.
One thing also, I'll have to use "getline" because there could be spaces on
the lines in these files.

Oh. I wanted to mention this, but somehow I forgot :)
 
H

Howard

Karl Heinz Buchegger said:
Best is often: read with getline, and look at the last character in the string.
If it is a '\n', discard it.

I've got a question on comparing a char against '\n'. Since on some
systems, '\n' is represented by #13#10 (a CR/LF pair), what happens when you
compare a char with it? Is there some internal mechanism that actually
compares two bytes instead of just one?

-Howard
 
R

Ron Natalie

Howard said:
I've got a question on comparing a char against '\n'. Since on some
systems, '\n' is represented by #13#10 (a CR/LF pair), what happens when you
compare a char with it? Is there some internal mechanism that actually
compares two bytes instead of just one?
'\n' is ALWAYS a single character. ALWAYS.

The only thing that happens is that under certain circumstances an output
operation may output two characters when an single '\n' character is
fed to it (and similarly on input).
 
H

Howard

Ron Natalie said:
'\n' is ALWAYS a single character. ALWAYS.

The only thing that happens is that under certain circumstances an output
operation may output two characters when an single '\n' character is
fed to it (and similarly on input).

Ah, that makes sense. Thanks.
-Howard
 
J

John Harrison

std::string one_line;
You used getline. So getline will include the terminator '\n' in the read
string. There is however one potential pitfall, the last line in the file
may not be terminated with '\n', thus the string will not contain it.

No it won't. getline reads the newline but does not include it in the read
string.

john
 
K

Karl Heinz Buchegger

John said:
No it won't. getline reads the newline but does not include it in the read
string.

Thanks for correcting.
I somehow always mix this up with fgets() behaviour
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top