By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,089 Members | 2,283 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,089 IT Pros & Developers. It's quick & easy.

Read text file into std::string.

P: n/a
Does this function need to call eof after the while-loop to be correct?

bool read_file(std::string name, std::string &s)
{
std::ifstream in(name.c_str());
if (!in.is_open())
return false;

char c;
std::string str;
while (in.get(c))
str += c;

if (!in.eof())
return false;

s = str;
return true;
}

Thanks.
Jul 23 '05 #1
Share this Question
Share on Google+
22 Replies


P: n/a
Jason Heyes wrote:
Does this function need to call eof after the while-loop to be correct?

bool read_file(std::string name, std::string &s)
{
std::ifstream in(name.c_str());
if (!in.is_open())
return false;

char c;
std::string str;
while (in.get(c))
str += c;

if (!in.eof())
return false;

s = str;
return true;
}


Calling EOF after the loop here is a check to insure that you exited
the loop because you read to the end of the file. There are a few other
reasons that get() might not be able to read a char and return a count
of 0.

Jul 23 '05 #2

P: n/a
Jason Heyes wrote:
Does this function need to call eof after the while-loop to be correct?
Yes.
bool read_file(std::string name, std::string &s)
{
std::ifstream in(name.c_str());
if (!in.is_open())
return false;

char c;
std::string str;
while (in.get(c))
str += c;
This loop is _extremely_ slow (or do you want to prove the
impracticality of C++?). Use getline (in, str) instead.

if (!in.eof())
return false;

s = str;
s.swap(str);
return true;
}


Jul 23 '05 #3

P: n/a
Me
> Does this function need to call eof after the while-loop to be correct?

Depends. The stream can terminate that while loop for a number of
reasons including a bad harddrive or an out of memory condition, etc.
If you want to make sure it read all the characters all the way to end
of the file, you have to check if it exited the loop because due to
eof.
bool read_file(std::string name, std::string &s)
{
std::string str;
<snip>
s = str;


btw, if this is your code, I suggest you do s.swap(str) instead of the
assignment.

Jul 23 '05 #4

P: n/a

"Jason Heyes" <ja********@optusnet.com.au> wrote in message
news:42***********************@news.optusnet.com.a u...
Does this function need to call eof after the while-loop to be correct?

bool read_file(std::string name, std::string &s)
{
std::ifstream in(name.c_str());
if (!in.is_open())
return false;

char c;
std::string str;
while (in.get(c))
str += c;

if (!in.eof())
return false;

s = str;
return true;
}

Thanks.


Why not extract the file's contents into a container of strings? If you make
the container a member of a class, read_file() and a write_file() could be
member functions of that class with access to the encapsulated container.

There are a number of other ways this could be done. You might overload the
write() member function so it takes a reference to another string container
to write to file, you might choose to use the same filestream to read /
write, etc.

// FileParser.cpp
//
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <algorithm>
#include <stdexcept>

class FileParser
{
// members
const std::string m_filename_;
std::string m_buffer;
std::ifstream m_ifs;
std::ofstream m_ofs;
std::vector<std::string> m_vs;
public:
// ctor
FileParser(std::string s)
: m_filename_(s), m_buffer(), m_ifs(), m_ofs(), m_vs() { }
~FileParser() { }

// read() member function
void read() throw(std::exception)
{
m_ifs.open(m_filename_.c_str(), std::ios::in);
if (!m_ifs)
{
throw std::exception("error opening file for read().\n");
}
while (std::getline(m_ifs, m_buffer))
{
m_vs.push_back(m_buffer);
}
if (!m_ifs.eof()) // if reason of termination != eof
{
throw std::exception("error while parsing file.\n");
}
}

// write(...) member function
void write() throw(std::exception)
{
m_ofs.open(m_filename_.c_str());
if (!m_ofs)
{
throw std::exception("error opening file for write().\n");
}
// copy algorithm with ostream_iterator
std::copy( m_vs.begin(),
m_vs.end(),
std::ostream_iterator<std::string>(m_ofs, "\n") );
}

// load(...) member function
void load(std::vector<std::string>& r_vs)
{
m_vs.clear();
std::copy(r_vs.begin(), r_vs.end(), std::back_inserter(m_vs));
}

// display() member function
void display() const
{
std::cout << "\n--- FileParse::display() ---\n\n";
std::copy( m_vs.begin(),
m_vs.end(),
std::ostream_iterator<std::string>(std::cout,
"\n") );
}

}; // class FileParser

int main()
{
std::vector<std::string> vstrings;
vstrings.push_back("string 0");
vstrings.push_back("string 1");
vstrings.push_back("string 2");
vstrings.push_back("string 3");
vstrings.push_back("string 4");

// FileParser object
FileParser fileparser("file.dat");
fileparser.load(vstrings);
fileparser.display();

try
{
fileparser.write();

fileparser.read();
fileparser.display();
}
catch (const std::exception& e)
{
std::cout << "Error:\n";
std::cout << e.what();
}

return 0;

} // main()

/* file.dat

--- FileParse::display() ---

string 0
string 1
string 2
string 3
string 4

*/
Jul 23 '05 #5

P: n/a
"Rapscallion" <ra********@spambob.com> wrote in message
news:11**********************@o13g2000cwo.googlegr oups.com...
Jason Heyes wrote:

char c;
std::string str;
while (in.get(c))
str += c;


This loop is _extremely_ slow (or do you want to prove the
impracticality of C++?). Use getline (in, str) instead.


Why is the loop slow? Isn't get inlined?

I read that getline needs a count of the number of elements to extract and
an array to store them. It does not take std::string. So how can I use
getline instead of get in this function?
Jul 23 '05 #6

P: n/a


Jason Heyes wrote:
"Rapscallion" <ra********@spambob.com> wrote in message
news:11**********************@o13g2000cwo.googlegr oups.com...
Jason Heyes wrote:

char c;
std::string str;
while (in.get(c))
str += c;


This loop is _extremely_ slow (or do you want to prove the
impracticality of C++?). Use getline (in, str) instead.


Why is the loop slow? Isn't get inlined?

I read that getline needs a count of the number of elements to extract and
an array to store them. It does not take std::string. So how can I use
getline instead of get in this function?


getline(in, str);

This reads the data until it finds either EOF, '\n' or max amount of
data that can be read is read (to check the last you need to see if the
fail bit is set).
It doesn't copy the '\n' (or EOF) into the string but it does extract
it so the code should be:
getline(in, str);
str+= '\n';

Jul 23 '05 #7

P: n/a
Peter Julian wrote:
Why not extract the file's contents into a container of strings? If you make
the container a member of a class, read_file() and a write_file() could be
member functions of that class with access to the encapsulated container.

There are a number of other ways this could be done. You might overload the
write() member function so it takes a reference to another string container
to write to file, you might choose to use the same filestream to read /
write, etc.
Your FileParser is done well but the design is not perfect. Some
remarks are attached. Refactoring the design is recommended.
// FileParser.cpp
//
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <algorithm>
#include <stdexcept>

class FileParser
{
// members
const std::string m_filename_;
std::string m_buffer;
std::ifstream m_ifs;
std::ofstream m_ofs;
These need not be members.

std::vector<std::string> m_vs;
Use std::list to avoid unnecessary copies of strings (each copy > 16
means dynamic allocation on VC 7.1)
std::vector<std::string> m_vlist;
public:
// ctor
FileParser(std::string s)
: m_filename_(s), m_buffer(), m_ifs(), m_ofs(), m_vs() { }
~FileParser() { }

// read() member function
void read() throw(std::exception)
{
m_ifs.open(m_filename_.c_str(), std::ios::in);
if (!m_ifs)
{
throw std::exception("error opening file for read().\n");
}
while (std::getline(m_ifs, m_buffer))
{
m_vs.push_back(m_buffer);
m_vlist.resize (m_vlist.size() +1);
m_vlist.back().swap (m_buffer);
}
if (!m_ifs.eof()) // if reason of termination != eof
{
throw std::exception("error while parsing file.\n");
}
}

// write(...) member function
void write() throw(std::exception)
void write (std::ostream& os) // write to any ostream
{
m_ofs.open(m_filename_.c_str());
if (!m_ofs)
{
throw std::exception("error opening file for write().\n");
}
// copy algorithm with ostream_iterator
std::copy( m_vs.begin(),
m_vs.end(),
std::ostream_iterator<std::string>(m_ofs, "\n") );
}

// load(...) member function
void load(std::vector<std::string>& r_vs)
why this???
{
m_vs.clear();
std::copy(r_vs.begin(), r_vs.end(), std::back_inserter(m_vs));
} // display() member function
void display() const
friend
std::ostream& operator<< (std::ostream& os, const FileParser& f);
{
std::cout << "\n--- FileParse::display() ---\n\n";
std::copy( m_vs.begin(),
m_vs.end(),
std::ostream_iterator<std::string>(std::cout,
"\n") );
}

}; // class FileParser

int main()
{
std::vector<std::string> vstrings;
vstrings.push_back("string 0");
vstrings.push_back("string 1");
vstrings.push_back("string 2");
vstrings.push_back("string 3");
vstrings.push_back("string 4");

// FileParser object
FileParser fileparser("file.dat");
fileparser.load(vstrings);
fileparser.display();

try
{
fileparser.write();

fileparser.read();
fileparser.display();
}
catch (const std::exception& e)
{
std::cout << "Error:\n";
std::cout << e.what();
}

return 0;

} // main()

/* file.dat

--- FileParse::display() ---

string 0
string 1
string 2
string 3
string 4

*/


Jul 23 '05 #8

P: n/a
Jason Heyes wrote:

Why is the loop slow? Isn't get inlined?
It's inlined probably, but still moves one character at a time
fom the stream and grows the string one element at a time with it.
I read that getline needs a count of the number of elements to extract and
an array to store them. It does not take std::string. So how can I use
getline instead of get in this function?

You didn't read far enough. There's a getline that's not a member
of stream that takes a string.
Jul 23 '05 #9

P: n/a
Ron Natalie wrote:
Jason Heyes wrote:

Why is the loop slow? Isn't get inlined?

It's inlined probably, but still moves one character at a time
fom the stream and grows the string one element at a time with it.


And you think getline does someting else internally?

Jul 23 '05 #10

P: n/a
Rolf Magnus wrote:
Ron Natalie wrote:
Why is the loop slow? Isn't get inlined?

It's inlined probably, but still moves one character at a time
fom the stream and grows the string one element at a time with it.


And you think getline does someting else internally?


Yes!

Jul 23 '05 #11

P: n/a
"Rapscallion" <ra********@spambob.com> wrote in message
news:11**********************@z14g2000cwz.googlegr oups.com...
Rolf Magnus wrote:
Ron Natalie wrote:
>> Why is the loop slow? Isn't get inlined?
>>
> It's inlined probably, but still moves one character at a time
> fom the stream and grows the string one element at a time with it.


And you think getline does someting else internally?


Yes!


Do you mind explaining how? :P
Jul 23 '05 #12

P: n/a
<ve*********@hotmail.com> wrote in message
news:11**********************@o13g2000cwo.googlegr oups.com...
getline(in, str);

This reads the data until it finds either EOF, '\n' or max amount of
data that can be read is read (to check the last you need to see if the
fail bit is set).
It doesn't copy the '\n' (or EOF) into the string but it does extract
it so the code should be:
getline(in, str);
str+= '\n';


Do you mind writing the loop as well? I can't see what you are doing.
You get an extra newline character added to the end of str with that code.
Jul 23 '05 #13

P: n/a
> Do you mind writing the loop as well? I can't see what you are doing.
You get an extra newline character added to the end of str with that code.


It wasn't meant as a complete function, just a comment on how to work
with it.
And yes you need that extra newline since the getline function doesn't
add it to the string but it does remove it from the stream.
But if you want a while loop, here is the whole function.

bool read_file(std::string name, std::string &s)
{
std::ifstream in(name.c_str());
if (!in.is_open())
{
return false;
}

std::string str;
while(in.good())
{
// guard against reading '\n' and then EOF
if (std::char_traits<char>::eof() != in.peek())
{
break;
}
getline(in, str);
str+= '\n';
}
if (!in.eof())
{
return false;
}
s.swap(str);
return true;
}

Jul 23 '05 #14

P: n/a
Jason Heyes wrote:
Do you mind explaining how? :P


Just don't add charactes one by one to the string. Do it in larger
chunks.

Jul 23 '05 #15

P: n/a


Rapscallion wrote:
Jason Heyes wrote:
Do you mind explaining how? :P


Just don't add charactes one by one to the string. Do it in larger
chunks.

Or how about just:

std::string text;

text << fileStream.rdbuf();

This seems most straightforward to me. No need to mess with loops and
what not.

-shez-

Jul 23 '05 #16

P: n/a
Shezan Baig wrote:
Or how about just:
std::string text;

text << fileStream.rdbuf();

This seems most straightforward to me. No need to mess with loops and
what not.


The 'mess with loops' and 'text << fileStream.rdbuf()' produce the same
result? Really?

Jul 23 '05 #17

P: n/a
"Shezan Baig" <sh************@gmail.com> wrote in message
news:11**********************@g49g2000cwa.googlegr oups.com...


Rapscallion wrote:
Jason Heyes wrote:
> Do you mind explaining how? :P


Just don't add charactes one by one to the string. Do it in larger
chunks.

Or how about just:

std::string text;

text << fileStream.rdbuf();

This seems most straightforward to me. No need to mess with loops and
what not.


How can that work? It looks like you were trying to write:

is.rdbuf() >> text;

This does not work.
Jul 23 '05 #18

P: n/a


Jason Heyes wrote:
How can that work? It looks like you were trying to write:

is.rdbuf() >> text;

This does not work.

Sorry, wasn't thinking. It should be:

std::stringstream ss;

ss << is.rdbuf();

now you can do whatever you want with 'ss.str()'.

hth,
-shez-

Jul 23 '05 #19

P: n/a
"Shezan Baig" <sh************@gmail.com> wrote in message
news:11**********************@z14g2000cwz.googlegr oups.com...


Jason Heyes wrote:
How can that work? It looks like you were trying to write:

is.rdbuf() >> text;

This does not work.

Sorry, wasn't thinking. It should be:

std::stringstream ss;

ss << is.rdbuf();

now you can do whatever you want with 'ss.str()'.


Hey it works! Thanks for the neat trick.
Jul 23 '05 #20

P: n/a
"Shezan Baig" <sh************@gmail.com> wrote in message
news:11**********************@z14g2000cwz.googlegr oups.com...


Jason Heyes wrote:
How can that work? It looks like you were trying to write:

is.rdbuf() >> text;

This does not work.

Sorry, wasn't thinking. It should be:

std::stringstream ss;

ss << is.rdbuf();

now you can do whatever you want with 'ss.str()'.


Here in.eof() equals false:

bool read_file(std::string name, std::string &s)
{
std::ifstream in(name.c_str());
if (!in.is_open())
return false;

std::stringstream ss;
ss << in.rdbuf();

if (!in.eof())
return false;

s = ss.str();
return true;
}

When a loop is used, in.eof() equals true. Why the difference?
Jul 23 '05 #21

P: n/a
Jason Heyes wrote:

Here in.eof() equals false:

bool read_file(std::string name, std::string &s)
{
std::ifstream in(name.c_str());
if (!in.is_open())
return false;

std::stringstream ss;
ss << in.rdbuf();

if (!in.eof())
return false;

s = ss.str();
return true;
}

When a loop is used, in.eof() equals true. Why the difference?

Because this code reads from the filebuf, which has no notion of eof,
badbit etc. If you need to check eof or other failures, you can do it
the other way round:

is >> ss.rdbuf();

hth,
-shez-

Jul 23 '05 #22

P: n/a
"Shezan Baig" <sh************@gmail.com> wrote in message
news:11**********************@g43g2000cwa.googlegr oups.com...
Jason Heyes wrote:

Here in.eof() equals false:

bool read_file(std::string name, std::string &s)
{
std::ifstream in(name.c_str());
if (!in.is_open())
return false;

std::stringstream ss;
ss << in.rdbuf();

if (!in.eof())
return false;

s = ss.str();
return true;
}

When a loop is used, in.eof() equals true. Why the difference?

Because this code reads from the filebuf, which has no notion of eof,
badbit etc. If you need to check eof or other failures, you can do it
the other way round:

is >> ss.rdbuf();


I tried this with the eof checking enabled. I lost all "\n\t" sequences.
Very peculiar.
Jul 23 '05 #23

This discussion thread is closed

Replies have been disabled for this discussion.