ifstream::getline() synatx

M

Mark S.

Hi,
since my next exercise involves working with files, I tried to learn
some more about ifstream. So far in the book, reading from files was
generally handled like this:
ifstream in(file); // (1)
string s;
while(getline(in, s)) // (2)
...

So far, so good. But when looking around, I also found the following syntax:
ifstream in; // (3)
string s;
in.open(file); // (4)
in.getline(s, 30); // (5)
...
in.close(); // (6)

Both works, of course. The second way of handling files made more sense
to me, but I was still wondering about the syntax. Google brought up
this page:
http://www.cplusplus.com/reference/iostream/ifstream/

Now my questions:

a) On the web page, it says: "The file to be associated with the stream
can be specified either as a parameter in the constructor or by calling
member open."
Now, the way I understand it is this: (1) is opening the file via the
constructor (and creates the object "in"?) and (3) is creating the
object first, then opening the file explicitly (4) and eventually
closing it (6).
In the documentation, I don't read anything about a destructor, so I wonder:
- Why don't I need to close the file in the first example?

b) My second (more important) question has to do with getline. With (2),
the _complete_ (!) line is automatically read into the string "line"
while the second syntax seems to always require a second argument - the
length that is to be read. This is they way I also interpret the
documentation, however, the first example clearly works. So..

- I thought both example used the same functions, just with different
syntax?
- How come that the documentation does not mention how to read the
complete line? Is that part missing? Shouldn't in.getline(s) work the
same way getline(in, s) does?

The documentation says that getline() exists in fstream and in string,
could that be the problem? If so, how do I resolve it (I mean, isn't it
pretty common to have strings when you are working with files?)?

-> Basically, which way should I use to handle files? Or am I
overlooking something?

Thank you for your time!

Kind regards
Mark
 
M

Mark S.

Obnoxious said:
Hi,
since my next exercise involves working with files, I tried to learn
some more about ifstream. So far in the book, reading from files was
generally handled like this:
ifstream in(file); // (1)
string s;
while(getline(in, s)) // (2)
...

So far, so good. But when looking around, I also found the following
syntax:
ifstream in; // (3)
string s;
in.open(file); // (4)
in.getline(s, 30); // (5)

istream& istream::getline (char* s, streamsize n )

You can't pass a std::string as an argument for 's'. It won't compile.
You need to allocate a buffer.

char buffer[20];
in.getline(buffer,20);
I see.
No they don't.
Ups, I compiled the second example without the actual getline. *blush*
All of this makes a lot more sense now!
The destructor of fstream will close the file if you haven't
done so yourself.
Yes, but what if I want to close it beforehand? Then I just in.close,
right? Also, I thought it is "good programming" to make sure these kind
of things are done right? So I guess my question should not only have
been whether or not it closes, but also if I should close in manually.
That's because std::getline() is an auxiliary function to make it easier
to read a text line from any input stream without having to worry about
buffer sizes.


istream& istream::getline (char* s, streamsize n )

This member function needs to know how much it can store in the buffer
pointed to by 's'. That's why you need to supply the size.


No, they use different functions.
Sorry again if this is a stupid question - but how do I make sure which
one is used? From my newbie perspective, both do the same (read a line
from a file). Moreover, how do I know which one is the better choice for
the task at hand?
 
J

James Kanze

[...]
Yes, but what if I want to close it beforehand? Then I just
in.close, right? Also, I thought it is "good programming" to
make sure these kind of things are done right? So I guess my
question should not only have been whether or not it closes,
but also if I should close in manually.

It depends. In principal, close can fail, so should not be left
to the destructor. In practice, when inputting, you're checking
the status after each input anyway; if close fails, there's
something seriously wrong with the system, and you can't do much
about it, so there's really no strong motivation for not just
leaving it to the destructor. For output, close() can fail even
though all earlier output succeeded, and in such cases, your
data may not be correctly written. At the very least, you have
to inform the user of this in some way---if you're writing to a
file, you may even want to delete the file, rather than leaving
corrupt data lying around. So an explicit close in the normal
program flow is essential. On the other hand, if some other
serious error occured, so that you're abandonning processing
anyway (and will report an error and maybe delete the file
anyway), then it probably doesn't matter if the data in the file
isn't valid, so you can allow the close in the destructor to do
the job. (In this case, the destructor would typically be
called as a result of stack unwinding from an exception.)

[...]
Sorry again if this is a stupid question - but how do I make
sure which one is used? From my newbie perspective, both do
the same (read a line from a file). Moreover, how do I know
which one is the better choice for the task at hand?

One's a member function, the other isn't, so the calling syntax
is different. And they take different types of arguments.

As to which is better, IMHO, there's never any valid reason to
input into a char[]. The member function is probably present
mainly for historical reasons---it was around before std::string
was invented.
 
M

Mark S.

Arghhh, this whole exercise is more and more difficult for me. I'm at a
total loss here. This is the exercise (TICPP, 7.1):
Create a Text class that contains a string object to hold the text of a
file. Give it two constructors: a default constructor and a constructor
that takes a string argument that is the name of the file to open. When
the second constructor is used, open the file and read the contents into
the string member object. Add a member function contents( ) to return
the string so (for example) it can be printed. In main( ), open a file
using Text and print the contents.

For testing purposes, I tried to implement the procedure in main() first.

Before said:
> ifstream in(file);

At that point I assumed I could easily define "file" like this:
string file = "E01.cpp";

But of course the argument needs to be a const char* (I still don't
really know what the difference between string and char* is - many
websites seem to use both terms simultaneously), so I need to convert it
first. Google brought up this (
http://www.codeguru.com/cpp/cpp/string/general/article.php/c13267 ):
char* name = new char[strlen("marius")+1];
strcpy(name, "marius");

but I can't get it to work. I guess the "+1" is for the terminating \0,
but strlen is problematic anyways, which is, I THINK, from cstring (?).
So I modified it:
string a = "E01.cpp";
char* b = new char[(a.length())+1];
But then I don't know how to copy the string!

The reference:
http://www.cplusplus.com/reference/string/string/copy/

says
size_t copy ( char* s, size_t n, size_t pos = 0) const;

Except the words "copy", "char* s" and "const", I have no idea what the
words mean and I don't understand the whole syntax of the line. Only
with the help of their description below, their example and Google I was
able to figure out some parts (but I still don't know why there is a
"const" at the end or why they use size_t instead of the unsigned int,
which it says it is). But seriously, how does one read this line??? How
do I go from not knowing anything about this function to writing a
working line of code? Is there a tutorial on how to read these things?

Anyways, in their example
size_t length;
char buffer[20];
string str ("Test string...");
length=str.copy(buffer,6,5);
buffer[length]='\0';
cout << "buffer contains: " << buffer << "\n";

the string is copied to the char array (which I guess is the same as
char* , right?) , but only 6 characters starting at position 5. Since
all parameters are required, *I don't see how just to copy a complete
string*, without defining the length first. I would have something like
this in mind:
string a = "can be very long or short, whatever";
char copyhere[] = a.length(); // or a.length + 1 ?
copyhere = a; || a.copy(copyhere); || copyhere = copy(a);
Or something like that. But nothing works and I am quite frustrated
right now so I would really appreciate any help you guys could give me.

Thank you in advance!

Kind regards
Mark
 
P

Phlip

Obnoxious said:
'char *'-strings are old school C strings. Avoid them if you're learning.
Use only std::string. It's a complete design failure that the fstream
classes don't accept std::string.

You really think the Committee didn't think of that? They set a precedent
against silent type promotions that cause more trouble than they are worth
at scaling time. Just type the freaking .c_str() yourself.
 
J

James Kanze

You really think the Committee didn't think of that? They set
a precedent against silent type promotions that cause more
trouble than they are worth at scaling time. Just type the
freaking .c_str() yourself.

He wasn't asking for an implicit conversion of std::string to
char const*. I think everyone pretty much understands why that
would be a bad thing. What surprised him is that
std::ifstream::eek:pen doesn't take a string as its argument. And
the reason is purely historical, and related to deadlines when
producing the last standard---the next version of the standard
has such an overload.
 
P

Phlip

James said:
He wasn't asking for an implicit conversion of std::string to char const*.

I didn't say he did. I wouldn't have overloaded open() myself, but the
Committee is certainly free too!
 
M

Mark S.

Obnoxious said:
'char *'-strings are old school C strings. Avoid them if you're learning.
Use only std::string. It's a complete design failure that the fstream
classes don't accept std::string.
Just to make sure: You mean that I should use c_str() whenever possible?
I am still confused when "some text" is treated as a string and when as
a char*. But I guess I'll figure it out eventually as time goes on.
You don't need to. You can ask the std::string object to return
a temporary c-string when necessary.

string s = "E01.cpp";
ifstream in(s.c_str());
if(in.good()) {
while(getline(in,s)) {
cout << s << endl;
}
}
Thank you, that worked. On to exercise 2! :p

I must say I do not quite understand the discussion that follows after
your post. Are you guys saying that C++ is still a work in progress?
 
M

Mark S.

James said:
[...]
The destructor of fstream will close the file if you haven't
done so yourself.
Yes, but what if I want to close it beforehand? Then I just
in.close, right? Also, I thought it is "good programming" to
make sure these kind of things are done right? So I guess my
question should not only have been whether or not it closes,
but also if I should close in manually.

It depends. In principal, close can fail, so should not be left
to the destructor. In practice, when inputting, you're checking
the status after each input anyway; if close fails, there's
something seriously wrong with the system, and you can't do much
about it, so there's really no strong motivation for not just
leaving it to the destructor. For output, close() can fail even
though all earlier output succeeded, and in such cases, your
data may not be correctly written. At the very least, you have
to inform the user of this in some way---if you're writing to a
file, you may even want to delete the file, rather than leaving
corrupt data lying around. So an explicit close in the normal
program flow is essential.
Thanks, this is what I thought.
On the other hand, if some other
serious error occured, so that you're abandonning processing
anyway (and will report an error and maybe delete the file
anyway), then it probably doesn't matter if the data in the file
isn't valid, so you can allow the close in the destructor to do
the job. (In this case, the destructor would typically be
called as a result of stack unwinding from an exception.)
Hmm, but I guess it would still be nice to tell the user what is going
on. Of course it all depends on the situation, but I think I will try to
explicitly close files for now.
 
M

Mark S.

Obnoxious said:
Any text string in double quotes is of type 'char const *'.
"text"
So when I have
string str = "some text";
there is actually some kind of cast happening?
C++ is standardized. But the next standard is in the pipeline.
I see. Thank you.
 
M

Mark S.

Hmm, I still have some trouble with files:

#include <fstream>
#include <iostream>
using namespace std;

int main()
{
string s;
ifstream in("E07.cpp");
getline(in, s);
cout << s << endl;
in.seekg(0,ios::beg); // (1) going back to beginning of file
getline(in, s); // works!
cout << s << endl;
in.close();

int i = 0, j = 0;
ifstream in2("E07.cpp");
while(getline(in2, s))
i++;
in2.seekg(0,ios::beg); // (2) does not work
while(getline(in2, s))
j++;
cout << j << endl; // j is still 0!

}

- Why does (1) work, but (2) doesn't??? More importantly, how do I get
it to work without opening the file a second time?

- But in case I do want to open a file, how can I open the same file
with the same identifier?
ifstream in("test.cpp");
in.close; // works
in.open // does not work
ifstream in("test.cpp"); // also doesn't work of course (double
declaration of "in")
 
M

Mark S.

Obnoxious said:
So when I have
string str = "some text";
there is actually some kind of cast happening?

No. In this assignment[1] 'str' will copy the c-string. std::string works
with c-strings transparently. Thats why you don't have to bother with
the lower levels of string management, std::string will do all that for
you. For example, you can't concatenate two c-strings the natural way:
char const * greeting = "Hello ";
greeting += "World";
But with std::string it will work:
std::string greeting = "Hello ";
greeting += "World";

[1] string::string(char const * s);
string::string& string::eek:perator=(char const * s);

Hmm, I think (hope) I understand (except the syntax of [1]) but I'm not
sure. I did realize that I did not phrase my question precise enough.

Please tell me if I'm wrong (actually, telling me when I'm right would
also be nice):
a) "some text" is the c-string (char const *) - an array of chars with a
'\0' at the end.
b) it is copied to str, which is an instance of a string object
c) For me as a user of std::string, a string is not an array of chars
anymore, but something else (what exactly I don't know yet but I hope I
will learn as my study progresses). All I need to know for now is that I
can easily do some stuff with it that would otherwise be more
complicated (and potentially dangerous).
d) Internally, std::string may (or may not) still use c-strings (like a
class of imaginary numbers may still use floats).
 
J

James Kanze

Any text string in double quotes is of type 'char const *'.
"text"

String literals have type char const[] in C++, not type char
const*. They will, of course, convert to char const* in most
contexts, they will also convert to std::string in many
contexts, and to char* in a few special contexts.

As for general rules as to when to use what, I'd argue that
anytime you have to declare a variable, it should be
std::string, and you'll use c_str() if you need to use a legacy
interface. On the other hand, I don't have any problem with:
std::ifstream in( "constantFileName" ) ;
rather than
std::ifstream in( std::string( "constantFileName" ).c_str() ) ;
 
J

James Kanze

Formally, there is a conversion, yes. If you write
string str( "some text" ) ;
however, there isn't.
No. In this assignment[1]

There is no assignment in his example.
'str' will copy the c-string. std::string works with c-strings
transparently.

Not totally. The conversion counts as a user defined
conversion, if it's implicit. So if I write something like:

class C
{
public:
C( std::string const& ) ;
} ;

C aC = "some text" ;

it won't compile.
 
J

James Kanze

Hmm, but I guess it would still be nice to tell the user what
is going on.

Presumably, you're going to tell him something, when you catch
the exception. Presumably, too, if there was a problem serious
enough to warrent an exception, the file you're writing probably
won't be usable, even if the close succeeds, so there's no point
in verifying whether it does succeed or not.
Of course it all depends on the situation, but I think I will
try to explicitly close files for now.

It's never wrong. For input, as I said, it is usually
superfluous.
 
J

James Kanze

Hmm, I still have some trouble with files:

Hmm, you overlooked one sentence in my previous posting:). You
check the status of an input stream after every operation.
#include <fstream>
#include <iostream>
using namespace std;
int main()
{
string s;
ifstream in("E07.cpp");

What happens if the open in the constructor fails?
getline(in, s);

What happens if the getline fails (perhaps because the open
beforehand failed)?
cout << s << endl;
in.seekg(0,ios::beg); // (1) going back to beginning of file
getline(in, s); // works!
cout << s << endl;
in.close();
int i = 0, j = 0;
ifstream in2("E07.cpp");
while(getline(in2, s))
i++;
in2.seekg(0,ios::beg); // (2) does not work
while(getline(in2, s))
j++;
cout << j << endl; // j is still 0!
}
- Why does (1) work, but (2) doesn't???

Because in (2), you've reached the end of file. An operation
failed, the stream object memorizes the error until you
explicitly reset it.
More importantly, how do I get
it to work without opening the file a second time?
istream::clear.

- But in case I do want to open a file, how can I open the same file
with the same identifier?
ifstream in("test.cpp");
in.close; // works
in.open // does not work
ifstream in("test.cpp"); // also doesn't work of course (double
declaration of "in")

You'd have to give the identifier a second time.

In addition, IIRC, there was an oversight in the standard, and
the sequence close and open do not reset any of the error bits.
Including the fact that the last input failed because you'd
reached end of file. So you likely need a clear between the
close and the open.
 
M

Mark S.

James said:
Hmm, you overlooked one sentence in my previous posting:). You
check the status of an input stream after every operation.
I'm sorry, but I have read the posts two more times now but I don't know
which sentence you are referring to.
What happens if the open in the constructor fails?


What happens if the getline fails (perhaps because the open
beforehand failed)?
So you mean additional error checking is necessary? What would be the
standard way of doing this?
Because in (2), you've reached the end of file. An operation
failed, the stream object memorizes the error until you
explicitly reset it.


istream::clear.
Alright, I have added the line ( in.clear(); ). But sadly, the result is
the same. :-(
You'd have to give the identifier a second time.
Sorry, I don't know what you mean by that.
 
J

James Kanze

I'm sorry, but I have read the posts two more times now but I
don't know which sentence you are referring to.

The sentence "In practice, when inputting, you're checking
the status after each input anyway;". You can't use the result
of input until you've verified that the input actually worked.
So you mean additional error checking is necessary?

Yes. If the file wasn't present, or was empty, or didn't
contain a newline character, getline will fail. And the
contents of s will not be signficant.
What would be the standard way of doing this?

if ( in ) {
// succeeded...
} else {
// failed...
}

Usually, you'll want to read more than just one line, and would
write something like:

while ( std::getline( in, s ) ) {
// ...
}

The most frequent reason for failure is that you've reached the
end of file, at least in the case of getline. For formatting
input, the issue is more complicated, since you can also have
format errors (i.e. you're reading an int, and the input is
"abc"). You can distinguish to some degree why the operation
failed by checking individual status bits:
in.bad() A hardware error. Not much you can do about
it (and many applications just treat it as
end of file).

in.fail() && ! in.eof()
A format error in the input. Tell the user.
In addition, you might want to try to
resynchronize and continue, by clearing the
error ("in.clear()"), reading data until you
think you'r resynchronized, and trying to
continue.

in.fail() && in.eof()
No more data was present. (Usually. There
are a few special cases where a format error
can also result in this situation.)
Alright, I have added the line ( in.clear(); ). But sadly, the
result is the same. :-(

Where did you add it. If you added it before the seek, it
should work.
Sorry, I don't know what you mean by that.

Me neither. Either some context is missing, or I was thinking
of something else. If you want to open a file a second time:

std::ifstream in( "test.cpp" ) ;
// ...
in.close() ;
in.clear() ;
in.open( "test.cpp" ) ;
// ...

The clear is formally necessary according to the current
standard. It won't be necessary in the next version of the
standard, and I think that some current implementations don't
require it either. (It doesn't make sense. close, followed by
open, means you're starting over.)

Alternatively, you can use a different variable:

{
std::ifstream in( "test.cpp" ) ;
// ...
}
{
std::ifstream in( "test.cpp" ) ;
// ...
}
 
M

Mark S.

James said:
The sentence "In practice, when inputting, you're checking
the status after each input anyway;". You can't use the result
of input until you've verified that the input actually worked.



Yes. If the file wasn't present, or was empty, or didn't
contain a newline character, getline will fail. And the
contents of s will not be signficant.


if ( in ) {
// succeeded...
} else {
// failed...
}
Ahh, that is simple enough. :)
Usually, you'll want to read more than just one line, and would
write something like:

while ( std::getline( in, s ) ) {
// ...
}
Yes, that's what I had so far.
The most frequent reason for failure is that you've reached the
end of file, at least in the case of getline. For formatting
input, the issue is more complicated, since you can also have
format errors (i.e. you're reading an int, and the input is
"abc"). You can distinguish to some degree why the operation
failed by checking individual status bits:
in.bad() A hardware error. Not much you can do about
it (and many applications just treat it as
end of file).

in.fail() && ! in.eof()
A format error in the input. Tell the user.
In addition, you might want to try to
resynchronize and continue, by clearing the
error ("in.clear()"), reading data until you
think you'r resynchronized, and trying to
continue.

in.fail() && in.eof()
No more data was present. (Usually. There
are a few special cases where a format error
can also result in this situation.)
Thank you!
Where did you add it. If you added it before the seek, it
should work.
Ahh, that was indeed the error. istream::clear had to come before
istream::seekg and istream::close.
Me neither. Either some context is missing, or I was thinking
of something else. If you want to open a file a second time:

std::ifstream in( "test.cpp" ) ;
// ...
in.close() ;
in.clear() ;
in.open( "test.cpp" ) ;
// ...
Ahh, alright. This works as well now!
The clear is formally necessary according to the current
standard. It won't be necessary in the next version of the
standard, and I think that some current implementations don't
require it either. (It doesn't make sense. close, followed by
open, means you're starting over.)

Alternatively, you can use a different variable:

{
std::ifstream in( "test.cpp" ) ;
// ...
}
{
std::ifstream in( "test.cpp" ) ;
// ...
}
I'm sorry, but I don't see a different variable here. But I think I got
it anyway. Again, thank you!
 

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

Similar Threads

Rearranging .ply file via C++ String Parsing 0
Crossword 2
TF-IDF 2
Error with ifstream and exceptions 71
getline and EOF question 18
ifstream errors 15
getline - refresher 2
getline questions 2

Members online

Forum statistics

Threads
473,961
Messages
2,570,130
Members
46,689
Latest member
liammiller

Latest Threads

Top