Kamal Thennakoon
Kamal Thennakoon

Reputation: 1849

when reading a file using c++, How to ignore a line

I want to read custom file structure and output them, also i want to ignore the lines which are not in the right format. Like comments, titles, etc.,

I've tried this code but its stop looping when it meets a line which is out of the structure.

Here is the txt file.

1001        Promod Dinal        IT-K20      42      42
1002        Sahan Navod         BM-K11      65      28

day_02

1003        Kaushani Dilinika   BM-K12      69      49
1004        Fathima Sahana      QS-K14      73      43  
int main()
{
    ifstream thefile;
    thefile.open("GameZone.txt");
    int id;
    char fName[30];
    char lName[30];
    char stream[30];
    int score;
    int time;

    if (!thefile.is_open()) {
        cout << "cant open the file" << endl;
    }
    else {
        while (!thefile.eof()) {
            if (thefile >> id >> fName >> lName >> stream >> score >> time) {
                cout << id << " ," << fName << " ," << lName << " ," << stream << " ," << score << " ," << time << endl;
            }else if(!(thefile >> id >> fName >> lName >> stream >> score >> time)){
                cout << "skip the row" << endl;
                continue;
            }
        }
    }
    return 0;
}

Output

1001 ,Promod ,Dinal ,IT-K20 ,42 ,42
1002 ,Sahan ,Navod ,BM-K11 ,65 ,28

Upvotes: 0

Views: 340

Answers (2)

David C. Rankin
David C. Rankin

Reputation: 84652

If you are still having difficulty with the implementation, then a very simple implementation would simple read each line, create a stringstream from the line to allow you to attempt to parse your values from, then depending on the result of reading from the stringstream output your values in your (rather strange " ,") csv format, or simply go read the next line and try again.

You should be using std::string instead of char[] to holds your string data in C++. Either will work, but the latter is much more user-friendly and flexible. In either case, you will want to coordinate all the differing types of data that make up one record as a struct. This has many benefits if you are actually doing something more than just dumping your data to stdout. For example, you can store all of your data read in a std::vector of struct and then be able to further process your data (e.g. sort, push_back, or erase records) as needed or pass it to other functions for further processing.

A simple struct using int and std::string could be:

struct record_t {       /* simple struct to coordinate data in single record */
    int id, score, time;
    std::string fname, lname, stream;
};

The reading and outputting of records in your csv format, can then be as simple as using a temporary struct to attempt to parse the line into, and if successful, output (or further use) the data as needed, e.g.

    std::string line;                       /* string to hold line */
    std:: ifstream fin (argv[1]);           /* in stream for file */

    while (getline (fin, line)) {           /* read entire line into line */
        std::stringstream ss (line);        /* create stringstream from line */
        record_t record;                    /* temp struct to read into */
        if (ss >> record.id >> record.fname >> record.lname >>
            record.stream >> record.score >> record.time)
            /* if successful read from stringstream, output record */
            std::cout << record.id << " ," << record.fname << " ," 
                    << record.lname << " ," << record.stream << " ," 
                    << record.score << " ," << record.time << '\n';
    }

Putting it altogether in a short example that takes the file to be read as the first argument to the program could be:

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>

struct record_t {       /* simple struct to coordinate data in single record */
    int id, score, time;
    std::string fname, lname, stream;
};

int main (int argc, char **argv) {

    if (argc < 2) { /* validate at least 1 argument provided */
        std::cerr << "error: filename required.\n";
        return 1;
    }

    std::string line;                       /* string to hold line */
    std:: ifstream fin (argv[1]);           /* in stream for file */

    while (getline (fin, line)) {           /* read entire line into line */
        std::stringstream ss (line);        /* create stringstream from line */
        record_t record;                    /* temp struct to read into */
        if (ss >> record.id >> record.fname >> record.lname >>
            record.stream >> record.score >> record.time)
            /* if successful read from stringstream, output record */
            std::cout << record.id << " ," << record.fname << " ," 
                    << record.lname << " ," << record.stream << " ," 
                    << record.score << " ," << record.time << '\n';
    }
}

(note: do not hard-code filenames or use magic numbers in your code)

Example Use/Output

Output in your rather odd " ," csv format:

$ ./bin/readrecords dat/records.txt
1001 ,Promod ,Dinal ,IT-K20 ,42 ,42
1002 ,Sahan ,Navod ,BM-K11 ,65 ,28
1003 ,Kaushani ,Dilinika ,BM-K12 ,69 ,49
1004 ,Fathima ,Sahana ,QS-K14 ,73 ,43

To make things slightly more useful, you can, instead of simply outputting the records directly, store all records in a std::vector<record_t> (vector of struct). This then opens the possibility of further processing your data. See if you can understand the changes made below on how each record is stored in a vector and then a Range-based for loop is used to loop over each record held in the vector to output your information.

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>

struct record_t {       /* simple struct to coordinate data in single record */
    int id, score, time;
    std::string fname, lname, stream;
};

int main (int argc, char **argv) {

    if (argc < 2) { /* validate at least 1 argument provided */
        std::cerr << "error: filename required.\n";
        return 1;
    }

    std::string line;                       /* string to hold line */
    std:: ifstream fin (argv[1]);           /* in stream for file */
    std::vector<record_t> records;          /* vector of records */

    while (getline (fin, line)) {           /* read entire line into line */
        std::stringstream ss (line);        /* create stringstream from line */
        record_t record;                    /* temp struct to read into */
        if (ss >> record.id >> record.fname >> record.lname >>
            record.stream >> record.score >> record.time)
            records.push_back(record);      /* if good read, add to vector */
    }

    if (records.size() > 0)         /* validate vector contains records */
        for (auto& r : records)             /* loop over all records */
            std::cout << r.id << " ," << r.fname << " ," << r.lname << " ,"
                    << r.stream << " ," << r.score << " ," << r.time << '\n';
    else    /* if no records read, throw error */
        std::cerr << "error: no records read from file.\n";
}

Look things over and let me know if you have any further questions.

Upvotes: 0

David Schwartz
David Schwartz

Reputation: 182883

Do not try to parse fields directly from the file. Instead, read lines from the file and attempt to parse those lines. Use the following algorithm:

  1. Read a line from the file.
  2. If you were not able to read a line, stop, you are done.
  3. Try to parse the line into fields.
  4. If you were not able to parse the line, go to step 1.
  5. Process the fields.
  6. Go to step 1.

Upvotes: 4

Related Questions