Search code examples
c++algorithmfilesearchc++98

To read only specific data from file... if string value is passed else to read everything from file c++98


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,

  1. when string value is passed std::string find = "pigeon"; will get information of pigeon. (working with current code)
  2. if empty value passed to string 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


Solution

  • " " 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;
    }