getline and EOF question

A

Amadeus W. M.

I'm trying to read a whole file as a single string, using the getline()
function, as in the example below. I can't tell what I'm doing wrong.
Tried g++ 3.2, 3.4 and 4.0. Thanks!


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

using namespace std;

int main(int argc, char * argv[])
{
const int MAX_SIZE=100000;
string s;
char chars[MAX_SIZE];

fstream IN("junk", ios::in);

// These 2 are ok.
IN.getline(chars, MAX_SIZE, EOF); // works, so EOF ok
getline(IN, s, 'x'); // works with string, no EOF

// g++ doesn't like this one.
getline(IN, s, EOF); // what's wrong??

return 0;
}
 
S

Steven T. Hatton

Amadeus said:
I'm trying to read a whole file as a single string, using the getline()
function, as in the example below. I can't tell what I'm doing wrong.
Tried g++ 3.2, 3.4 and 4.0. Thanks!


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

using namespace std;

int main(int argc, char * argv[])
{
const int MAX_SIZE=100000;
string s;
char chars[MAX_SIZE];

fstream IN("junk", ios::in);

// These 2 are ok.
IN.getline(chars, MAX_SIZE, EOF); // works, so EOF ok
getline(IN, s, 'x'); // works with string, no EOF

// g++ doesn't like this one.
getline(IN, s, EOF); // what's wrong??

return 0;
}

Unfortunately, there is a std::getline() declared as follows:

template<class charT, class traits, class Allocator>
basic_istream<charT,traits>& getline(basic_istream<charT,traits>& is,
basic_string<charT,traits,Allocator>& str, charT delim);

It wants a charT which is not of the same type as EOF. Try calling the
getline on IN. I have not tried that myself, so I don't know if it will
help.
 
A

Amadeus W. M.

Amadeus said:
I'm trying to read a whole file as a single string, using the getline()
function, as in the example below. I can't tell what I'm doing wrong.
Tried g++ 3.2, 3.4 and 4.0. Thanks!


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

using namespace std;

int main(int argc, char * argv[])
{
const int MAX_SIZE=100000;
string s;
char chars[MAX_SIZE];

fstream IN("junk", ios::in);

// These 2 are ok.
IN.getline(chars, MAX_SIZE, EOF); // works, so EOF ok
getline(IN, s, 'x'); // works with string, no EOF

// g++ doesn't like this one.
getline(IN, s, EOF); // what's wrong??

return 0;
}

Unfortunately, there is a std::getline() declared as follows:

template<class charT, class traits, class Allocator>
basic_istream<charT,traits>& getline(basic_istream<charT,traits>& is,
basic_string<charT,traits,Allocator>& str, charT delim);

It wants a charT which is not of the same type as EOF. Try calling the
getline on IN. I have not tried that myself, so I don't know if it will
help.

I did, in the very program I posted, and it worked. I was curious why the
one on string wouldn't work.

There is also a getline() in bits/basic_string.h

template<typename _CharT, typename _Traits, typename _Alloc>
basic_istream<_CharT,_Traits>&
getline(basic_istream<_CharT, _Traits>& __is,
basic_string<_CharT, _Traits, _Alloc>& __str, _CharT __delim);

So in this version, the type of the delimiter must be the same _CharT
as in basic_istream and basic_string. The same is true for the istream
version of getline. So I don't understand why one works and the other
doesn't. What's the type of EOF? Whatever the type, it should both work,
or both not work.
 
K

Kyle Kolander

Amadeus W. M. said:
I'm trying to read a whole file as a single string, using the getline()
function, as in the example below. I can't tell what I'm doing wrong.
Tried g++ 3.2, 3.4 and 4.0. Thanks!


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

using namespace std;

int main(int argc, char * argv[])
{
const int MAX_SIZE=100000;
string s;
char chars[MAX_SIZE];

fstream IN("junk", ios::in);

// These 2 are ok.
IN.getline(chars, MAX_SIZE, EOF); // works, so EOF ok
getline(IN, s, 'x'); // works with string, no EOF

// g++ doesn't like this one.
getline(IN, s, EOF); // what's wrong??

return 0;
}

Here is a small example of one way to read the entire file. The key to
answering your question is checking the state of the stream. Once you have
read the last line of the file, it is the next call to getline() that will
set the eof flag of ifl.

#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char* argv[])
{
ifstream ifl("junk");
string s, temp;
while ( 1 )
{
getline(ifl, temp);
if (ifl.eof()) break;
s += temp + "\n"; // if you want to maintain line breaks
}
cout << "// starts on next line\n";
cout << s;
cout << "// ended on last line\n";
return 0;
}

On an aside, if you just want to read the entire contents of the file,
perhaps consider using read() on a binary file stream. You can open it at
the end of the file to deduce the size in bytes of the file. Then seek back
to the beginning and call read().

Thanks,
Kyle
 
P

Peter Gordon

I'm trying to read a whole file as a single string, using the getline ()
function, as in the example below. I can't tell what I'm doing wrong.
Tried g++ 3.2, 3.4 and 4.0. Thanks!
If the objective is to read a file into a string, the
code below is handy and instructive. It does not use
getline.

/*
From: Robert W Hand <[email protected]>
Newsgroups: alt.comp.lang.learn.c-c++
Subject: Re: Reading a file into a string?
Date: Wed, 30 Mar 2005 21:18:08 -0500

*/
#include <iostream>
#include <fstream>
#include <istream>
// #include <sstream>
#include <string>
#include <iterator>

int main(int argc, char *argv[]) {
if(argc != 3) {
std::cerr << "Usage: " << argv[0] << " InputFile OutputFile" <<
std::endl;
return 1;
}
std::ifstream fin(argv[1], std::ios::in);
if(fin.bad() ) {
std::cerr << "Could not open" << argv[1] << "for reading." <<
std::endl;
return 1;
}
std::eek:fstream fout(argv[2], std::ios::eek:ut | std::ios::binary);
if(fout.bad() ) {
std::cerr << "Could not open" << argv[2] << "for writing." <<
std::endl;
return 1;
}

fin.unsetf(std::ios_base::skipws);
std::istream_iterator<char> in(fin);
std::istream_iterator<char> out;
std::string buf(in, out); // Problem Line for bcc
fout << buf;
fin.close();
fout.close();

return 0;
}
 
A

Amadeus W. M.

I'm trying to read a whole file as a single string, using the getline()
function, as in the example below. I can't tell what I'm doing wrong.
Tried g++ 3.2, 3.4 and 4.0. Thanks!


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

using namespace std;

int main(int argc, char * argv[])
{
const int MAX_SIZE=100000;
string s;
char chars[MAX_SIZE];

fstream IN("junk", ios::in);

// These 2 are ok.
IN.getline(chars, MAX_SIZE, EOF); // works, so EOF ok
getline(IN, s, 'x'); // works with string, no EOF

// g++ doesn't like this one.
getline(IN, s, EOF); // what's wrong??

return 0;
}


std::getline(IN, s, (char) EOF); // works.

The std is necessary, because there's a C version in stdlib.h:

ssize_t getline(char **lineptr, size_t *n, FILE *stream);

and the compiler thinks I'm trying to call that one. Also, without the
cast to char, EOF is being treated as int. Don't know though why I don't
have to cast it in the stream version of getline.

So there, reading a file into a string can be done in one instruction.
 
L

Larry I Smith

Amadeus said:
I'm trying to read a whole file as a single string, using the getline()
function, as in the example below. I can't tell what I'm doing wrong.
Tried g++ 3.2, 3.4 and 4.0. Thanks!


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

using namespace std;

int main(int argc, char * argv[])
{
const int MAX_SIZE=100000;
string s;
char chars[MAX_SIZE];

fstream IN("junk", ios::in);

// These 2 are ok.
IN.getline(chars, MAX_SIZE, EOF); // works, so EOF ok
getline(IN, s, 'x'); // works with string, no EOF

// g++ doesn't like this one.
getline(IN, s, EOF); // what's wrong??

return 0;
}


std::getline(IN, s, (char) EOF); // works.

The std is necessary, because there's a C version in stdlib.h:

ssize_t getline(char **lineptr, size_t *n, FILE *stream);

and the compiler thinks I'm trying to call that one. Also, without the
cast to char, EOF is being treated as int. Don't know though why I don't
have to cast it in the stream version of getline.

So there, reading a file into a string can be done in one instruction.

Just be aware that this may work for text files, but probably
won't work for binary files - where "(char) EOF" (0xff) may
appear within the binary file.

Larry
 
S

Steven T. Hatton

Amadeus said:
Amadeus said:
I'm trying to read a whole file as a single string, using the getline()
function, as in the example below. I can't tell what I'm doing wrong.
Tried g++ 3.2, 3.4 and 4.0. Thanks!


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

using namespace std;

int main(int argc, char * argv[])
{
const int MAX_SIZE=100000;
string s;
char chars[MAX_SIZE];

fstream IN("junk", ios::in);

// These 2 are ok.
IN.getline(chars, MAX_SIZE, EOF); // works, so EOF ok
getline(IN, s, 'x'); // works with string, no EOF

// g++ doesn't like this one.
getline(IN, s, EOF); // what's wrong??

return 0;
}

Unfortunately, there is a std::getline() declared as follows:

template<class charT, class traits, class Allocator>
basic_istream<charT,traits>& getline(basic_istream<charT,traits>& is,
basic_string<charT,traits,Allocator>& str, charT delim);

It wants a charT which is not of the same type as EOF. Try calling the
getline on IN. I have not tried that myself, so I don't know if it will
help.

I did, in the very program I posted, and it worked.

What you posted was using an array of char with the member function. I
tried passing a string to the member function, and it didn't work. I
believe it could be made to work if the size of the file were passed to
getline, but I'm not sure how to get the size of the file, and haven't
found time to look into it.
I was curious why the one on string wouldn't work.

In terms of the Standard, I believe my reply explained that. Don't you
agree?
There is also a getline() in bits/basic_string.h

template<typename _CharT, typename _Traits, typename _Alloc>
basic_istream<_CharT,_Traits>&
getline(basic_istream<_CharT, _Traits>& __is,
basic_string<_CharT, _Traits, _Alloc>& __str, _CharT __delim);

I don't recommend looking in bits for definitive answers to the C++ Standard
Library API, but it can be instructive in both gaining a better
understanding of the API, and seeing how it is implemented.
So in this version, the type of the delimiter must be the same _CharT
as in basic_istream and basic_string. The same is true for the istream
version of getline. So I don't understand why one works and the other
doesn't. What's the type of EOF? Whatever the type, it should both work,
or both not work.

The errors I'm getting are suggesting that EOF is type int. You could try
casting to char or something, but that doesn't seem like a portable
solution, even if it does work. As to why it works in one case and not the
other, I would really have to examine all the signatures. I've extracted
most of the Standard Headers from the Standard, and created source code
files. Unfortunately there are a few remaining to be done. Among them are
some of the IO headers.

Just as a guess, the could be a case where a template is being used in one
circumstance, and not the other. Templates do not support type conversion
in the same was as do comperable built in types.

As for whether it /should/ work for both or neither, I agree that is
desirable, but I don't believe the Standard dictates that, and it may be an
unreasonable expectation of the implementers. I haven't seen a function
signature in the Standard that would necessarily work for any form using
EOF. If it does work in one circumstance, that's just luck of the draw.
 
S

Steven T. Hatton

Amadeus said:
I did, in the very program I posted, and it worked. I was curious why the
one on string wouldn't work.

One thing I neglected to mention in previous replies - but which I suspect
we both agree upon - is that getline() is really not intended to be used in
this way, and there are better means of reading in a whole file. This
discussion is merely "academic". Do you agree?
 
L

Larry I Smith

Steven said:
One thing I neglected to mention in previous replies - but which I suspect
we both agree upon - is that getline() is really not intended to be used in
this way, and there are better means of reading in a whole file. This
discussion is merely "academic". Do you agree?

The istream methods read() and readsome() would seem to be better
choices for the task.

Larry
 
A

Amadeus W. M.

What you posted was using an array of char with the member function. I
tried passing a string to the member function, and it didn't work.

Why would you expect it to work, if the prototype of the
istream::getline() is

istream& getline( char* buffer, streamsize num );
istream& getline( char* buffer, streamsize num, char delim );

string is not char*.
I believe it could be made to work if the size of the file were passed
to getline, but I'm not sure how to get the size of the file, and
haven't found time to look into it.

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <inttypes.h>
#include <sys/stat.h>
#include <unistd.h>

using namespace std;

int main(int argc, char * argv[])
{

// Use the stat system call.
struct stat status;
stat(argv[1], &status);
std::cout << status.st_size << std::endl;

// Alternatively, use seekg+tellg
size_t pos;
fstream IN;

IN.open(argv[1], ios::in | ios::binary);
IN.seekg(0, ios::end);
pos = IN.tellg();
IN.seekg(0, ios::beg); // or IN.close();
cout << pos << endl;

return 0;
}

In terms of the Standard, I believe my reply explained that. Don't you
agree?

What's the standard got to do with it? I'm not saying the standard if
violated. In

istream::getline(char*, streamsize, char delim);

EOF is treated as char, whereas in

std::getline(istream&, string &, char delimiter='\n');

EOF is treated as int (as you noticed). Notice that I used
IN.getline(...), with fstream IN. Now fstream is an instantiation of
basic_fstream to char:

pwd
/usr/include/c++/3.4.0
grep fstream * | grep typedef | grep -v wchar
iosfwd: * typedef basic_ifstream<char> ifstream;
iosfwd: typedef basic_ifstream<char> ifstream; ///< @isiosfwd
iosfwd: typedef basic_ofstream<char> ofstream; ///< @isiosfwd
iosfwd: typedef basic_fstream<char> fstream; ///< @isiosfwd

So then istream::getline() is not templated, and int to char is implicit.
On the other hand, std::getline is templated, and the conversion is not
implicit. Your intuition was right.
 
A

Amadeus W. M.

One thing I neglected to mention in previous replies - but which I suspect
we both agree upon - is that getline() is really not intended to be used in
this way, and there are better means of reading in a whole file. This
discussion is merely "academic". Do you agree?

To paraphrase a famous president: it depends what the meaning of the word
"better" is. If by better you mean faster/more efficient, then of course,
I agree.

But e.g. for ASCII files of size=O(10k) on which you want to do
some find-and-replace, putting the entire file in a string in single
instruction is a pretty darn good way of doing it.

I don't suppose anyone in their right mind would try to read a 1G vob file
in a string, would they?
 
S

Steven T. Hatton

Amadeus said:
What you posted was using an array of char with the member function. I
tried passing a string to the member function, and it didn't work.

Why would you expect it to work, if the prototype of the
istream::getline() is

istream& getline( char* buffer, streamsize num );
istream& getline( char* buffer, streamsize num, char delim );

string is not char*.
I believe it could be made to work if the size of the file were passed
to getline, but I'm not sure how to get the size of the file, and
haven't found time to look into it.

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <inttypes.h>
#include <sys/stat.h>
#include <unistd.h>

using namespace std;

int main(int argc, char * argv[])
{

// Use the stat system call.
struct stat status;
stat(argv[1], &status);
std::cout << status.st_size << std::endl;

// Alternatively, use seekg+tellg
size_t pos;
fstream IN;

IN.open(argv[1], ios::in | ios::binary);
IN.seekg(0, ios::end);
pos = IN.tellg();
IN.seekg(0, ios::beg); // or IN.close();
cout << pos << endl;

return 0;
}

In terms of the Standard, I believe my reply explained that. Don't you
agree?

What's the standard got to do with it? I'm not saying the standard if
violated. In

istream::getline(char*, streamsize, char delim);

EOF is treated as char, whereas in

std::getline(istream&, string &, char delimiter='\n');

EOF is treated as int (as you noticed). Notice that I used
IN.getline(...), with fstream IN. Now fstream is an instantiation of
basic_fstream to char:

pwd
/usr/include/c++/3.4.0
grep fstream * | grep typedef | grep -v wchar
iosfwd: * typedef basic_ifstream<char> ifstream;
iosfwd: typedef basic_ifstream<char> ifstream; ///<
@isiosfwd
iosfwd: typedef basic_ofstream<char> ofstream; ///<
@isiosfwd
iosfwd: typedef basic_fstream<char> fstream; ///<
@isiosfwd

So then istream::getline() is not templated, and int to char is implicit.
On the other hand, std::getline is templated, and the conversion is not
implicit. Your intuition was right.
Just as a guess, the could be a case where a template is being used in
one
circumstance, and not the other. Templates do not support type
conversion in the same was as do comperable built in types.

Strange. In both cases the type of the delimiter is actually specified as a
template parameter. In the case of std::basic_istream<> it's a template
parameter of the class. In the std::getline() it's a template parameter of
the function. I'm not sure why one works and not the other.

In /usr/include/c++/3.3.5/string, bits/basic_string.h is the file being
#included. That means the std::getline in question is declared as follows:

template<typename _CharT, typename _Traits, typename _Alloc>
basic_istream<_CharT,_Traits>&
getline(basic_istream<_CharT, _Traits>& __is,
basic_string<_CharT, _Traits, _Alloc>& __str, _CharT __delim);

Now /usr/include/c++/3.3.5/istream defines basic_istream like this:

template<typename _CharT, typename _Traits>
class basic_istream : virtual public basic_ios<_CharT, _Traits>
{
public:
// Types (inherited from basic_ios (27.4.4)):
typedef _CharT char_type;

//...

__istream_type&
getline(char_type* __s, streamsize __n, char_type __delim);
//...
};


The error message I get is this:

test.cpp: In function `int main(int, char**)':
test.cpp:21: error: invalid conversion from `void*' to `char**'
test.cpp:21: error: cannot convert `std::string' to `size_t*' for argument
`2'
to `__ssize_t getline(char**, size_t*, FILE*)'//wth is this?

That makes me believe its trying to use std::istream::getline(). When I
explicitly wrote std::getline(IN, s, EOF);

I got:
test.cpp: In function `int main(int, char**)':
test.cpp:21: error: no matching function for call to `getline(std::fstream&,
std::string&, int)'
 
S

Steven T. Hatton

Larry said:
The istream methods read() and readsome() would seem to be better
choices for the task.

Larry
This is my choice:

ifstream in("filename");
stringstream ss;
ss << in.rdbuf();
 
A

Amadeus W. M.

Strange. In both cases the type of the delimiter is actually specified as a
template parameter. In the case of std::basic_istream<> it's a template
parameter of the class. In the std::getline() it's a template parameter of
the function. I'm not sure why one works and not the other.

Now you understand my confusion. Read my previous post more carefully.
fstream is in fact an instantiation of basic_fstream to char, so
istream::getline() is just a regular function now, and so the implicit
conversion from int to char of EOF. However, std::getline remains
templated, and the type of EOF must match that of string, and they don't.

The error message I get is this:

test.cpp: In function `int main(int, char**)': test.cpp:21: error:
invalid conversion from `void*' to `char**' test.cpp:21: error: cannot
convert `std::string' to `size_t*' for argument `2'
to `__ssize_t getline(char**, size_t*, FILE*)'//wth is this?

I explained this before: it's the C-version of getline. The compiler
finds this one first and complains. It will work if you use std::getline.
 
S

Steven T. Hatton

Amadeus said:
Now you understand my confusion. Read my previous post more carefully.
fstream is in fact an instantiation of basic_fstream to char, so
istream::getline() is just a regular function now, and so the implicit
conversion from int to char of EOF. However, std::getline remains
templated, and the type of EOF must match that of string, and they don't.

Hey, I just checked the dates on some of my first C++ work since I got
serious about it, I can only claim about 12 mos of solid C++ experience.
What you posted was a bit cryptic for me.

I believe what you are saying is that the compiler doesn't have to figure
out what char means when it process the function call because it already
did that when it compiled the template for fstream. The instantiated
template is just another class at this point. Right?
 
O

Old Wolf

Amadeus said:
std::getline(IN, s, (char) EOF); // works.

The std is necessary, because there's a C version in stdlib.h:

ssize_t getline(char **lineptr, size_t *n, FILE *stream);

and the compiler thinks I'm trying to call that one.

std::getline is a function template:
template<class charT, class traits, class Allocator>
basic_istream<charT, traits>& getline(
basic_istream<charT, traits>&,
basic_string<charT, traits, Allocator>&,
charT delim );

If you call it with parameters:
basic_istream<char, traits>
basic_string<char, traits, Allocator>
int

as you have done, then the compiler cannot match this to that
particular function template. It won't automatically try
and convert 'int' to everything to see if it does get a match.

So it tries to match your call to the non-template version of
getline, and reports its failures there. (This is the
behaviour mandated by the C++ standard; although some compilers
will be friendly and warn you that it failed to match the
template version too).
Also, without the cast to char, EOF is being treated as int.

EOF is an int that is returned by the C standard IO functions
in <cstdio>.

EOF is not a char. It is never returned by any C++ stream I/O
functions, and cannot be passed as a parameter to them. In fact
it has nothing to do with stream I/O at all.
Don't know though why I don't have to cast it in the stream
version of getline.

Usually EOF is defined to be -1. So your call looks like this:
std::getline(IN, s, -1);

This reads from IN into the string 's', until it encounters
the character -1 (usually the byte 255).
If your file didn't contain this character, then it would
appear to read until the end of file.
So there, reading a file into a string can be done in one instruction.

A more robust way:

std::eek:stringstream oss;
oss << IN.rdbuf();

Then oss.str() returns the string.

Another good way is the method suggested by Peter Gordon.
 
O

Old Wolf

Peter said:
fin.unsetf(std::ios_base::skipws);
std::istream_iterator<char> in(fin);
std::istream_iterator<char> out;
std::string buf(in, out); // Problem Line for bcc
fout << buf;
fin.close();
fout.close();

BCC 5.5.1 has a bug with its std::string constructors.
Don't know if it's fixed in later versions.
You can use this instead:

std::string buf;
std::copy(in, out, std::back_inserter(buf));
 
A

Amadeus W. M.

Don't know though why I don't have to cast it in the stream
Usually EOF is defined to be -1. So your call looks like this:
std::getline(IN, s, -1);

I meant istream::getline(). This also expects a char as its 3rd argument
(delimiter), and it's also templated ;)
How come it accepts EOF, which is int? That was my question here.

Thanks for the answer, very thorough! And the rdbuf() call is very handy
too.
 

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,962
Messages
2,570,134
Members
46,690
Latest member
MacGyver

Latest Threads

Top