0

I have a file named "SCORES.TXT" that contains this:

Player      - Score
--------------------
John Miles  - 132
Henry       - 90
Juliet P    - 110

The program must show to the user the person name and the respective score, like:

John Miles has a score of 132
Henry has a score of 90
Juliet P has a score of 110

I have the following code but it is not working properly. The variable nickname only gets the first name, and if I add a string variable to get the second name, the programm will not work in the lines that only have one name.

#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
int main()
{
    string fileName = "SCORES.TXT", line, nickname;
    char c;
    unsigned int score;
    unsigned short int i = 0;
    ifstream file(fileName);
    while (getline(file, line)) { 
        if (i < 2) {              //
            i++;                  // ignoring the header
        }                         //
        else {
            stringstream s(line);
            s >> nickname >> c >> score;
            cout << nickname  << " has a score of " << score;
        }
    }
    file.close();
    return 0;
}
rique002
  • 1
  • 1
  • 3
    Pretty sure stringstream's operator >> will stop at the first whitespace character so your nickname will be incomplete in some cases and the rest of the extraction will either not do what you want or just fail. i would suggest finding the position of - in the line and then extracting each half based on that info. I assume this is what you meant by "not working properly" since you didnt actually specify what it is that wasn't working. – Borgleader May 19 '21 at 12:08
  • ***and I cannot understand why*** You need to get a debugger like gdb or the one in Visual Studio. With a debugger you can step through your code 1 line at a time looking at your variables at each step. – drescherjm May 19 '21 at 12:09
  • Obs: each name is less than 16 characters – rique002 May 19 '21 at 12:09
  • The length is not a problem. `John Miles` is a problem if you are going to use >> nickname – drescherjm May 19 '21 at 12:11
  • 1
    More generally, when you write new code that does several new things, *test them one at a time.* You ought to have noticed that `nickname` captured `John` and not `John Miles` *before* you attempted to capture the score. And please see the page on ["how to ask a good question"](https://stackoverflow.com/help/how-to-ask). – Beta May 19 '21 at 12:12
  • `std::getline` has an optional third parameter, I wonder what it could be used for. – n. m. could be an AI May 19 '21 at 13:39

1 Answers1

0

One solution to scan a complex input line is to use std::regex This is very flexible but maybe not very efficient.

Some hints:

You should not use using namespace std;

file.close() is not explicitly needed, as at end of scope of main the ifstream go out of scope and will be destructed automatically which also closes the file but it is not a failure to manually close it before.

See the following example how to use regex to scan a string for patterns and how to fetch the search results:

Quite clear: This is only one of thousand possible solutions! It is very flexible but less efficient as already mentioned.

#include <string>
#include <iostream>
#include <fstream>
#include <regex>

int main()
{
    std::string fileName = "SCORES.TXT";
    std::string line;
    // we search the input line for the following regular expression:
    // first everything which is a charater -> a-z or A-Z and a space and as mutch as we find *
    // after that we search for - and also the following whitspaces
    // next we pick the number
    // the () arround the expression forwards the result of each () in a separate result m[]
    std::regex re("([a-zA-Z ]*)[- ]*([0-9]*)", std::regex::extended );

    std::ifstream file(fileName);
    while (std::getline(file, line)) 
    {
        try  // if regex did not find correct patterns, we ignore that
        {
            std::smatch m;
            std::regex_search( line, m, re );

            // we expect 3 parts as result, first is the full string, second is our nickname, last is the number
            if ( m.size() == 3 ) 
            {
                // as we have also the trailing whitespaces in the nickname part, we remove them here
                std::string nickname = std::regex_replace(std::string(m[1]), std::regex(" +$"), "");
                // create an int from found number string
                int number = std::stoi(m[2]);

                std::cout << nickname << " has a score of " << number << std::endl;
            }
        } catch(...){}
    }   

    return 0;
}

You can test online regular expressions which might help to get femiliar with the complex syntax. There are a lot sites to help you! Take care that some regular expressions are a bit different or not available in some languages.

Klaus
  • 24,205
  • 7
  • 58
  • 113
  • I had never heard of std::regex. Thank you very much. Just a quetion, why shouldn't we use "using namespace::std" ? Makes the program less efficient? – rique002 May 19 '21 at 15:40
  • @rique002 there is a lot of good info here: [https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) – drescherjm May 19 '21 at 21:05
  • @rique002 Instead of say Tanks, please use the UpVote and if the answer solves your problem, please accept the answer. That is how SO works! :-) Thanks! – Klaus May 20 '21 at 07:39
  • @rique002 Why not using namespace: Simply because especially in `std` are very much is defined. There is a good chance, that you will define own things with the same name which will result in name conflicts. Even if that is not happen today, in the STL will be added more functionality from time to time. Maybe with next compiler update your will not compile because your definitions collide with the ones in namespace `std::` – Klaus May 20 '21 at 07:42