I have file named bird.lst. I am trying to read its contents and store the data map data structure.
Where i am looking to read specific bird information,
std::string find = "pigeon";
will get information of pigeon
. (working with current code)std::string find = " ";
, then should able to get all information of birds.(Not working)I am kind off stuck, not able to figure out how to proceed. Suggestions are welcomed thanks in advance.
i have mentioned examples when std::string find
is passed with empty or bird value with expected output...
bird.cpp
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include <utility>
typedef std::pair<std::string,std::string> attribute_pair;
typedef std::vector<attribute_pair> attribute_vector;
typedef std::map<std::string,attribute_vector> bird_map;
int main()
{
std::ifstream file("bird.lst");
std::string find = " ";
bird_map birds;
std::string key;
while(std::getline(file,key))
{
std::size_t pos = key.find(find);
attribute_vector attributes;
std::string value;
if(pos != std::string::npos && key.substr(pos) == find) {
while(std::getline(file,value))
{
// in case it has windows encoding with end-of-line = \r\n
if (!value.empty() &&
value[value.size()-1] == '\r')
{
value.erase(value.size() - 1);
}
// if we found the empty string
if(value.empty())
{
break;
}
// now split the value into an attribute and a flag
attribute_pair attribute;
std::istringstream ss(value);
ss >> attribute.first >> attribute.second;
if(attribute.first != "vaccinated" && attribute.first != "babies" && attribute.first != "sale") {
// save the value into the vector
attributes.push_back(attribute);
}
}
// save the bird into the map
birds[key] = attributes;
}
}
// now print the data we collected
for(bird_map::iterator bird = birds.begin();
bird != birds.end();
bird++)
{
std::cout << bird->first << "\n";
for(attribute_vector::iterator attribute = bird->second.begin();
attribute != bird->second.end();
attribute++)
{
std::cout << " " << attribute->first
<< " " << attribute->second
<< "\n";
}
std::cout << "\n";
}
return 0;
}
bird.lst
parrot
vaccinated yes
eat yes
babies no
fly yes
sale no
pigeon
vaccinated yes
eat yes
fly yes
babies yes
sale yes
duck
vaccinated yes
eat yes
fly no
sale yes
babies no
flammingo
vaccinated yes
eat yes
fly yes
sale no
babies no
eagle
vaccinated yes
eat yes
babies no
fly yes
when std::string find="duck";
which is working with existing code.
duck
eat yes
fly no
when std::string find=" ";
where it should get data for all birds
duck
eat yes
fly no
eagle
eat yes
fly yes
flammingo
eat yes
fly yes
parrot
eat yes
fly yes
pigeon
eat yes
fly yes
" "
is not an empty string, ""
is. Nor are you checking for an empty string before searching anyway.
In any case, when find()
fails to find a match, you are skipping your inner while
loop altogether, and thus not skipping that current bird's info. So the next outer loop iteration misinterprets those unread attributes as if they were bird names, which is wrong.
I suggest writing a function that can read a bird's complete info at a time from the file, and then call that function in a loop. On each iteration, if the search string is truly empty, or if it matches the current bird's name, then use that bird's info as needed. And in the latter case, you can then break the loop.
The code you posted as an answer was a good attempt at implementing such a function, but the code still has problems. Aside from it not even fixing the problem of skipping the reading of attributes for non-matching birds, you are splitting the responsibility of reading and processing a bird's details between main()
and readBird()
in an awkward way. main()
is reading each bird's name, then readBird()
is reading the bird's attributes AND inserting the bird into the map
. The sole responsibility of reading a complete bird, name and attributes, should be inside of readBird()
only, and then main()
should decide what to do with each complete bird that is output, as needed.
With that said, try something more like this instead:
#include <fstream>
#include <iostream>
#include <sstream>
#include <map>
#include <string>
#include <vector>
#include <utility>
typedef std::pair<std::string, std::string> attribute_pair;
typedef std::vector<attribute_pair> attribute_vector;
typedef std::map<std::string, attribute_vector> bird_map;
bool readLine(std::istream &in, std::string &s)
{
if (!std::getline(in, s)) return false;
if ((!s.empty()) && (s.back() == '\r')) s.pop_back();
return true;
}
bool readBird(std::istream &in, std::string &name, attribute_vector &attributes)
{
name.clear();
attributes.clear();
std::string value;
attribute_pair attribute;
do {
if (!readLine(in, value)) return false;
}
while (value.empty());
name = value;
while (readLine(in, value) && !value.empty()) {
std::istringstream iss(value);
if ((iss >> attribute.first) && ((attribute.first != "vaccinated") && (attribute.first != "babies") && (attribute.first != "sale"))) {
iss >> attribute.second;
attributes.push_back(attribute);
}
}
return true;
}
int main()
{
std::ifstream file("bird.lst");
std::string name, find;
attribute_vector attributes;
bird_map birds;
std::cout << "What would you like to search for? (leave blank for all):" << endl;
std::getline(std::cin, find);
while (readBird(file, name, attributes))
{
if (find.empty() || (name.find(find) != std::string::npos))
birds[name] = attributes;
}
for(bird_map::iterator bird = birds.begin();
bird != birds.end();
++bird)
{
std::cout << bird->first << "\n";
for(attribute_vector::iterator attribute = bird->second.begin();
attribute != bird->second.end();
++attribute)
{
std::cout << " " << attribute->first << " " << attribute->second << "\n";
}
std::cout << "\n";
}
return 0;
}