0

I'm trying to create a searchable recipe database by ingredient for a project. I'm trying to create the for loop that goes through the string vector (which has each ingredient saved to it) and search through the file and compare them. Right now, I just want it to output "Hello!" if theres a match. With all my fiddling, theres either 100 Hello!s (definitely not right) or none. Here's the code:

int main()
{
int y;
cout << "Hello! Welcome to Abby's Recipe Calculator." << endl << endl;
cout << "Please select an option: 1 to search by ingredient or 2 to browse recipes..." << endl;
cin >> y;

vector <string> ingreds;
ingreds.reserve(4); 

if (y == 1)
{
    ingredientvector(ingreds); // calls function to fill vector w/ ingredients
}
//else if (y == 2)
//{
//call recipe function... 
//}

Search x1(ingreds); //assigns ingredients to object vector

recipesearch(x1.getingreds());

system("pause");
return 0;
}

void ingredientvector(vector<string>& x)
 {
cout << "SEARCH BY INGREDIENT" << endl;
cout << "Please enter up to three ingredients... " << endl;

for (int i = 0; i < 4; i++)
{
    x.push_back("  ");
    getline(cin, x[i]);
    if (x[i] == "1")
    {
        break;
    }

}

  }

  void recipesearch(const vector<string>& ingredientlist) //ifstream& recipes)
 {

ifstream myrecipes;
string line;
string ingredient;
myrecipes.open("recipes.txt");
if (myrecipes.is_open())
{
    for (int i = 0; i < 4; i++)
    {
        ingredient = ingredientlist[i];
        while(getline(myrecipes, line)){
            if (ingredient == line)
            {
                cout << "Hello!" << endl;
            }
            else
            {
                break;
            }
        }
    }   
}
else cout << "Unable to open recipe file!";

myrecipes.close();
}

Here is an example of a recipe used:

Cheese-y Ramen

Prep Time: 5 minutes
Cook Time: 20 minutes
Total Time: 25 minutes
Servings: 2
Ingredients:
8 oz cheddar cheese
1 tablespoon cornstarch
¾ cup milk
2 packages ramen noodles
Directions:
1. Grate cheddar cheese and add with cornstarch into a small bowl
2. Combine with milk in a medium saucepan and cook on medium to low heat until consistent. Keep warm until serving.
3. In a separate pan boil ramen noodles. Set aside the included flavor packets.
4. Once boiling, drain the noodles and combine with cheese.
Recipe from Buzzfeed
Flimzy
  • 60,850
  • 13
  • 104
  • 147
  • 1
    Can you add an example of your recipe file. – Ayo I Nov 30 '15 at 23:09
  • After one iteration of `i` you are at the end of your file, and all following iterations will immediately break since there is nothing left to read. Try reversing your loops. – Jongware Nov 30 '15 at 23:10
  • Ah, yes. Jongware is correct. That would be a problem. – Ayo I Nov 30 '15 at 23:11
  • Off topic suggestion: rather than using a `std::vector` for `ingredientlist`, use a [`std::set`](http://en.cppreference.com/w/cpp/container/set) and use [set's `find` method](http://en.cppreference.com/w/cpp/container/set/find) instead of the `for` loop. This will scale better as the ingredient list grows. – user4581301 Nov 30 '15 at 23:14
  • thank you for that suggestion, but unfortunately I must use vectors for the project – Abby O'Connor Nov 30 '15 at 23:26
  • In that case, look at [`std::find`](http://en.cppreference.com/w/cpp/algorithm/find). It will do the same thing, only slower. – user4581301 Nov 30 '15 at 23:34
  • 1
    Looking at the above, you'll probably need to use something more like string::find, http://www.cplusplus.com/reference/string/string/find/ unless the user is typing in "1 tablespoon cornstarch". Also, make sure you're dealing with the fact that in C++ indexes start from zero. So three ingredients will only need: 0, 1, and 2 to access all ingredients in the array. But you're going up to 3 via i < 4. – Ayo I Nov 30 '15 at 23:42
  • Note: because you are reading line-by-line, you will not be looking in the ingredient list for "cheddar cheese", you will be looking for "8 oz cheddar cheese". – user4581301 Nov 30 '15 at 23:44
  • any tips on fixing these things? i really appreciate all the help. i've been on this for hours – Abby O'Connor Nov 30 '15 at 23:50
  • Seeing what you're dealing with, @user497745 has the right idea. Take a look at [this question here](http://stackoverflow.com/questions/2602013/read-whole-ascii-file-into-c-stdstring) to read the whole file into a string, then use the `string::find` method to look for for each entry in `ingredientslist`. Caveat: a lot of files you'll pull from the web are in wide characters, not the 7bit ascii used by default by most of the standard tools. – user4581301 Nov 30 '15 at 23:50
  • 1
    I've been working on a recipe database system for many years. I highly recommend using a real, external database. Messing with a data file is a waste of productivity. – Thomas Matthews Dec 01 '15 at 00:02
  • Almost certainly the truth, but this is almost certainly a homework assignment. That said, I'd love to see the TA's face after a fully functional database-driven solution is dropped on their desk. Mark that, sucker. – user4581301 Dec 01 '15 at 00:05

2 Answers2

1

This reads the entire recipe file into a string, then looks inside the string for each ingredient. Note: This is extremely brute force. It's fast, but not going to be very accurate. For example, if Cheetos are mentioned in a side bar, not in the recipe itself, they will still be listed.

Credit where it's due, this answer lifts the file read wholesale from Read whole ASCII file into C++ std::string

void recipesearch(const vector<string>& ingredientlist)
{

    ifstream myrecipes;
    string file;
    myrecipes.open("recipes.txt");
    if (myrecipes.is_open())
    {
        // read entire file into string
        myrecipes.seekg(0, std::ios::end);
        file.reserve(myrecipes.tellg());
        myrecipes.seekg(0, std::ios::beg);

        file.assign((std::istreambuf_iterator<char>(myrecipes)),
                    std::istreambuf_iterator<char>());

        // look inside file string for each ingredient
        for (const string & ingredient: ingredientlist)
        {

            if (file.find(ingredient) != file.npos)
            { // found ingredient in file
                cout << ingredient << endl;
            }
        }
    }
    else
        cout << "Unable to open recipe file!";

}

Caveat: A lot of files you'll pull from the web are in encoded in a multi-byte character set to get prettier results and internationalization, not the 7 bit ASCII used by default by most of the standard C++ tools, including the those used in the above example code.

Correctly interpreting which of potentially many multi-byte character sets to use and how to consume them is a discussion topic unto itself, but for the purposes of this assignment OP may be able to get away with ensuring all input files are saved with ASCII encoding.

Community
  • 1
  • 1
user4581301
  • 29,019
  • 5
  • 26
  • 45
  • This was extremely useful! was the file.reserve(myrecipes.tellg()) supposed to be "myrecipes.reverse....."? – Abby O'Connor Dec 01 '15 at 00:33
  • I realize you are only quoting the title "Read whole ASCII file into C++ std::string" and that code might indeed work, but the question's data is not ASCII. (Presumably, the encoding is the "system default" so if the file is saved as "system default" and the program that reads is running on a system (or user settings) with the same default encoding, it could work.) – Tom Blodget Dec 01 '15 at 01:47
  • Extremely important point. I made it in a comment above and really should have brought it up here. Thanks @TomBlodget – user4581301 Dec 01 '15 at 01:54
0

Try inverting the while and the for loop like such:

...the code before your for loop    
while(getline(myrecipes, line))
{
    for (int i = 0; i < 4; i++)
    {
        ingredient = ingredientlist[i];
        if (ingredient == line)
        {
            cout << "Hello!" << endl;
        }
        else
        {
            break;
        }
    }
}
...the code after your for loop
Ayo I
  • 6,642
  • 4
  • 24
  • 37
  • should i upload the text file itself? it's just a simple text file with recipes listed and dived by dashes – Abby O'Connor Nov 30 '15 at 23:17
  • Actually I was wrong about that break. Since it's not a contains, but an exact match, there can only be one correct match per for loop iteration, so removed my additional modification. – Ayo I Nov 30 '15 at 23:18
  • Well, most recipes are something like, 3 cups of sugar. So if your ingredients just say, "sugar" rather than, "3 cups of sugar" an exact match will work. But if you're really trying to see if "sugar" is in the ingredient line, then you're going to need to do a contains. – Ayo I Nov 30 '15 at 23:20
  • Yeah I've thought about that, I just want to make sure it can even search before I make the program more specific – Abby O'Connor Nov 30 '15 at 23:21
  • Go ahead and try the code posted above and see if that works. – Ayo I Nov 30 '15 at 23:22
  • also it is still doing the same output... the for loop definitely needed to change though. thank you for that part! maybe it's a different issue in the code – Abby O'Connor Nov 30 '15 at 23:22
  • When you say same output, what do you mean? – Ayo I Nov 30 '15 at 23:24
  • outputting "Hello!" dozens of times for a string that appears once or twice in the entire recipes database – Abby O'Connor Nov 30 '15 at 23:25
  • Will probably need to see your ingredient list and sample file – Ayo I Nov 30 '15 at 23:31
  • Maybe add just a piece of your recipes.txt file to your question – Ayo I Nov 30 '15 at 23:31
  • Also, try printing out the ingredient, so you can see what's being reported as a match. – Ayo I Nov 30 '15 at 23:37
  • It does print out just an ingredient, and it also definitely is reading the file. Not too sure whats going on otherwise. – Abby O'Connor Nov 30 '15 at 23:39