0

I am attempting to create a function that asks the user for a dvd title they're looking for which is located in a text file of the format:

title:genre:price

There are 10 lines in the text file which all follow that format. I first placed each line into an array with 10 elements.

I have a DVD class that has the function getTitle() which I'm trying to create:

void DVD::getTitle(){
    cout << "Type the title of the dvd you're looking for." << endl;
    cin >> title;

    for(int i = 0; i < 10; i++){ //Loop through the dvd array object.

        if(dvd[i].find(title)){ //Test if the title entered is in any of the array elements
            cout << "We have " << title << " in stock." << endl;
        }
    }
    cout << "Sorry we don't have that title." << endl;
}

This function is called in main with a DVD class object. I know the find function returns an iterator to the element once it's found, but I can't figure out how to print out the element once it is found. My output simply writes the first word of the title ("Free" in Free Willy) 9 times instead of only when it finds Free Willy in one of the array elements. I also tried using

for(int i = 0; i < 10; i++){ //Loop through the dvd array object.

    //Attempt to search each array element up to the colon, and place the 
    //strings before the colon in dvdTest.
    getline(dvd[i], dvdTest, ':');

    //Test if the string elements before the colon are the same as the title entered.
    if(dvdTest == title){
        cout << "We have " << title << " in stock." << endl;
    }
}
cout << "Sorry we don't have that title." << endl;

To try and get all of the current array element up to the colon placed into the dvdTest variable, I then tested if(dvdTest == title) before printing out whether the title is in stock. I got an error saying there is no matching function for call to getline, so I figured getline doesn't apply to arrays. I then tried

for(int i = 0; i < file.eof(); i++){ //Loop through the lines in the file.

    //Get strings from the current line up to the colon, place into dvdTest.
    getline(file, dvdTest, ':');

    if(dvdTest == title){ //Test if dvdTest and the title entered are the same.
        cout << "We have " << title << " in stock." << endl;
    }
}
cout << "Sorry we don't have that title." << endl;

I tried typing Avatar (the 5th title in the text file) and it simply output "Sorry we don't have that title.", so it either didn't find Avatar or it's checking the first line of the file every time it goes through the for loop? Is it possible to accomplish what I'm trying to do in a similar way, or is this the completely wrong approach and I should be doing it a different way?

I've checked all over cplusplus a few hours a day for 3 days now on file usage, getline, find, and I can't figure anything out.

MaxV
  • 3
  • 5
  • 1
    Right after the getline(), add a print statement and print out the value of dvdTest so you can verify what you're grabbing from the file. – Jason Enochs Jul 13 '17 at 23:06
  • If your array or vector is sorted, consider using `binary_search`, `upper_bound`, and `lower_bound` as well as `find`. For small data quantities, the significance between binary searches and linear searches is negligible. – Thomas Matthews Jul 13 '17 at 23:27
  • Recommend a read of [some good documentation](http://en.cppreference.com/w/cpp/io/basic_ios/eof) to refresh your memory on what is returned by `std::ios::eof` because `for(int i = 0; i < file.eof(); i++)` just looks nuts. – user4581301 Jul 14 '17 at 00:07
  • @user4581301 Yea that was my last for loop I tried since I was getting super desperate because I couldn't find anything anywhere so I ended up trying something willy nilly. – MaxV Jul 16 '17 at 19:54

3 Answers3

1

Put the strings into a vector and utilize the std::find function:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

int main(){
    std::vector<std::string> v{ "Movie 1", "Movie 2", "Movie 3" };
    std::cout << "Enter the movie title to find: ";
    std::string whattofind;
    std::getline(std::cin, whattofind);
    auto found = std::find(v.begin(), v.end(), "Movie 2");
    if (found != std::end(v)) {
        std::cout << "v contains: " << whattofind << '\n';
    }
    else {
        std::cout << "v does not contain: " << whattofind << '\n';
    }
}

If you want to use an array then utilize the std::begin and std::end functions followed by a std::find:

std::string movies[] = { "Movie 1", "Movie 2", "Movie 3" };
std::string whattofind = "Movie 2";
auto found = std::find(std::begin(movies), std::end(movies), "Movie 2");
if (found != std::end(movies)) // ... same as above

You can not accept a string from the standard input using the std::cin alone. You need to utilize the std::getline function instead:

std::string tempstr;
std::getline(std::cin, tempstr);
Ron
  • 14,674
  • 4
  • 34
  • 47
1

If you are using a c string, const char*, you must use strcmp(str1, str2):

#include <string>
...
bool found = false;

//Loop through the dvd array object
for(int i = 0; !found && i < 10; i++){ 
    //Is the title entered in any of the array elements?  
    found = (strcmp(dvd[i].title, title) == 0;
}

if (found)
    cout << "We have " << title << " in stock." << endl;
else
    cout << "Sorry we don't have that title." << endl;

Otherwise, just compare the std::strings using ==:

//Loop through the dvd array  object.
for(int i = 0; !found && i < 10; i++){ 
    //Is the title entered in any of the array elements?  
    found = (dvd[i].title == title);
}

if (found)
    cout << "We have " << title << " in stock." << endl;
else
    cout << "Sorry we don't have that title." << endl;
Lifka
  • 249
  • 1
  • 7
  • 17
1

The problem is that cin >> title does not read in a whole line. It only reads characters until it reaches whitspace.

To read in a whole lines of input you have to use:

std::string line;
while ( std::getline(cin, line) ) 
{
    // .... do something with line
}

How to do something with the line? (hyperlinks in the code)

1) You can use line.find(':') to find the semi colon positions and then use line.substr(pos_from, char_count) to extract the sub-strings.

2) You can use regular expressions std::regex("([^:]*):([^:]*):([^:]*)") to split this string up. And then use regex_match to extract the particular pieces.

3) Other options ...


Edit: To explain the regex (tutorial)

  1. First parenthesis - Match everything which is not a colon, this is matching group 1
  2. Match a colon
  3. Second parenthesis - Match everything which is not a colon, this is matching group 2
  4. Match a colon
  5. Third parenthesis - Match everything which is not a colon, this is matching group 3

Code:

// Extraction of a sub-match (matching group) 
const std::regex match_regex("([^:]*):([^:]*):([^:]*)");
std::smatch matched_strings;

if (std::regex_match(line, matched_strings, match_regex)) {
     std::string first_match = matched_strings[1].str();
     std::string second_match = matched_strings[2].str();
     std::string third_match = matched_strings[3].str();
}
Robert Andrzejuk
  • 5,076
  • 2
  • 22
  • 31
  • I can't believe I forgot that cin only goes up to the first whitespace, I'm extremely new as you've probably guessed haha. I'm interested in the your second method there, since I'm trying to make getTitle() now, but later on I'll want getGenre() and getPrice() as well, so I feel like I'll be forced to use regex and regex_match. I can tell what regex is doing, but I don't understand any of the syntax provided for regex_match. If regex splits the string into 3 components like that, when I want to match the title, genre, or price provided by the user, how do I use regex_match to do that? – MaxV Jul 16 '17 at 21:21
  • Added an explaination of the regex – Robert Andrzejuk Jul 20 '17 at 20:40