Click to See Complete Forum and Search --> : Checking for empty .txt file
Mybowlcut
February 20th, 2008, 12:48 AM
#include <fstream>
#include <iterator>
#include <algorithm>
#include <string>
// Reads data from a file.
// - Requires that Object has an overloaded >> operator.
// - Returns a Container of Objects.
// - Returns early if file is empty.
template <typename Object, typename Container>
Container read_data(std::istream& file)
{
Container v;
if(file.eof())
{ // Empty; return early.
return v;
}
std::copy(
std::istream_iterator<Object>(file),
std::istream_iterator<Object>(),
std::back_inserter(v));
return v;
}Hey. I've got this function.. I'm trying to test if the file passed in is completely empty... if it is, I return early for certain reasons. I'm just wondering how I can do this in the most elegant way... what I have above (in bold) doesn't work. I debugged it and the if statement body never gets reached.
Cheers.
code_carnage
February 20th, 2008, 01:11 AM
#include <fstream>
#include <iterator>
#include <algorithm>
#include <string>
// Reads data from a file.
// - Requires that Object has an overloaded >> operator.
// - Returns a Container of Objects.
// - Returns early if file is empty.
template <typename Object, typename Container>
Container read_data(std::istream& file)
{
Container v;
if(file.eof())
{ // Empty; return early.
return v;
}
std::copy(
std::istream_iterator<Object>(file),
std::istream_iterator<Object>(),
std::back_inserter(v));
return v;
}Hey. I've got this function.. I'm trying to test if the file passed in is completely empty... if it is, I return early for certain reasons. I'm just wondering how I can do this in the most elegant way... what I have above (in bold) doesn't work. I debugged it and the if statement body never gets reached.
Cheers.
I think You dont need to check if file is empty.
I think you sould use insert member function of the container.
v.insert( std::istream_iterator<Object>(file), std::istream_iterator<Object>());
Mybowlcut
February 20th, 2008, 01:23 AM
I took the read_data idea from Paul McKenzie, so I don't really understand the internals of copy and the istream_iterators. It works, so I use it, but I have a situation where the >> operator is being called when the file is empty and I don't want this to happen. I'm guessing it's due to the workings of copy... Would insert prevent the >> operator being called when the file is empty? If not I still need a solution. :o
Zaccheus
February 20th, 2008, 02:58 AM
One way to tell the size is to use seekg and tellg:
Something like this:
size_t FileSize(std::istream& stream)
{
stream.seekg(0, std::ios::end); // Move to end of the file
size_t result = stream.tellg(); // Ask where we are now.
stream.seekg(0, std::ios::beg); // Move back to beginning of file.
return result;
}
See also:
http://www.cplusplus.com/reference/iostream/istream/seekg.html
http://www.cplusplus.com/reference/iostream/istream/tellg.html
:)
angelorohit
February 20th, 2008, 03:08 AM
Originally posted by MyBowlcut:
I took the read_data idea from Paul McKenzie, so I don't really understand the internals of copy and the istream_iterators. It works, so I use it...The arguments to std::copy() are
1. An input iterator that addresses the starting position in the source
from which you want to copy
2. Another input iterator that addresses the ending position in the source
3. An output iterator that addresses the starting position in the
destination to which you want to copy.
std::copy(
std::istream_iterator<Object>(file),
std::istream_iterator<Object>(),
std::back_inserter(v));
Now, what we are passing to std::copy are :
1. istream_iterator that is initialized to the stream from which we want to
read. That's the starting position in our source.
2. A default istream_iterator that initializes to end-of-stream by default.
That's the end position in our source. So, the objects will be copied from
the input file until end of file is reached. You don't need to explicitly
check for end-of-file.
3. Makes an iterator that can insert elements at the back of the container. Using this iterator requires that the container have a push_back() method which means that we can only use deques, lists or vectors.
That's all there is to it.
Some sample code...See if this works. Also create an empty text file - Test.txt in your source directory.
#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <string>
#include <vector>
// Reads data from a file.
// - Requires that Object has an overloaded >> operator.
// - Returns a Container of Objects.
// - Returns early if file is empty.
template <typename Object, typename Container>
Container ReadData(std::istream& file)
{
Container v;
/*
//We don't need this.
if(file.eof())
{ // Empty; return early.
return v;
}
*/
std::copy(
std::istream_iterator<Object>(file),
std::istream_iterator<Object>(),
std::back_inserter(v));
return v;
}
int main()
{
std::ifstream file("Test.txt");
std::vector<char> vFile;
vFile = ReadData<char, std::vector<char> >(file);
for(int i = 0; i < vFile.size(); ++i)
std::cout<<vFile[i];
file.close();
return 0;
}
code_carnage
February 20th, 2008, 03:17 AM
std::copy is allright..
But I guess using container.assign(it1, it2); is better...
Rule of thumb is: Any action which is done with pair of iterator is better than the one achieved with one iterator..
With std::copy approach you will effectively be calling push_back which will do many reallocation and one function call for every element being added in the container..
While when you use container::assign() with pair of iterator.
I think the required space will already be created and no re-allocation will happen. Moreover you will have only one function call.
so I think its better to use container.assign(it1, it2) or container.insert(container.begin(),it1,it2); than copy here...
Correct me if I am wrong..
Zaccheus
February 20th, 2008, 04:04 AM
I don't think insert can determine the number of elements from an istream_iterator (note also that the second iterator is simply default constructed).
code_carnage
February 20th, 2008, 04:22 AM
I don't think insert can determine the number of elements from an istream_iterator (note also that the second iterator is simply default constructed).
Assign and Insert both take input iterator and istream_iterator is very much input iterator.
When given a pair of iterator to assign of insert generally it creates memory large enough to hold the range.
yes you may be right..But the question remains: Then how insert or assign works?
Mybowlcut
February 20th, 2008, 05:14 AM
The arguments to std::copy() are
1. An input iterator that addresses the starting position in the source
from which you want to copy
2. Another input iterator that addresses the ending position in the source
3. An output iterator that addresses the starting position in the
destination to which you want to copy.
std::copy(
std::istream_iterator<Object>(file),
std::istream_iterator<Object>(),
std::back_inserter(v));
Now, what we are passing to std::copy are :
1. istream_iterator that is initialized to the stream from which we want to
read. That's the starting position in our source.
2. A default istream_iterator that initializes to end-of-stream by default.
That's the end position in our source. So, the objects will be copied from
the input file until end of file is reached. You don't need to explicitly
check for end-of-file.
3. Makes an iterator that can insert elements at the back of the container. Using this iterator requires that the container have a push_back() method which means that we can only use deques, lists or vectors.
That's all there is to it.
Some sample code...See if this works. Also create an empty text file - Test.txt in your source directory.
#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <string>
#include <vector>
// Reads data from a file.
// - Requires that Object has an overloaded >> operator.
// - Returns a Container of Objects.
// - Returns early if file is empty.
template <typename Object, typename Container>
Container ReadData(std::istream& file)
{
Container v;
/*
//We don't need this.
if(file.eof())
{ // Empty; return early.
return v;
}
*/
std::copy(
std::istream_iterator<Object>(file),
std::istream_iterator<Object>(),
std::back_inserter(v));
return v;
}
int main()
{
std::ifstream file("Test.txt");
std::vector<char> vFile;
vFile = ReadData<char, std::vector<char> >(file);
for(int i = 0; i < vFile.size(); ++i)
std::cout<<vFile[i];
file.close();
return 0;
}
Thanks for that explanation, angelorohit.
In this case I do need to check the size of the file before proceeding, though.
Thanks, Zaccheus. That will do just fine. :)
angelorohit
February 20th, 2008, 05:27 AM
Originally posted by code_carnage:
When given a pair of iterator to assign of insert generally it creates memory large enough to hold the range.
What the insert() of a particular container does depends on a number of things. In this case, since our source iterator is an input interator, it will not pre-allocate anything, simply because it doesn't know how many objects it has to insert. As it reads each object from the file, it will make allocations accordingly. Hence, in this case, insert() is no better off than std::copy(). Of course, I am only talking about the Microsoft compiler's implementation of STL. Other compilers may do things differently. From what I've seen, some overloaded definitions of insert() even end up invoking std::copy().
code_carnage
February 20th, 2008, 05:32 AM
Yes.
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.