awyeanah2
awyeanah2

Reputation: 148

reading from text file to struct vector, but text file lines are of different length

I am trying to read from a text file of students and assign each line to a struct data member.

The text file structure is as follows:

Name,Student code, Ability,Consistency,Program name:Subject list

Two students in the file:

Average Ant,204932,50,5,Short course:1
Brilliant Bison,234543,80,3,Bachelor of Bounciness:2,5,3

I can read all of the information to a struct no problem except for the last part(subject list),that are of varying length between students. How can I write code so that if a student only has 1 subject like average ant I can push it into the subjectList vector but if they have 3 like brilliant bison I can still push them all in no problem?

My code:

struct Student
{
    string name;
    int code;
    int ability;
    int consistency;
    string programName;
    vector<int> subjectList;
};

void createStudents(string fileName)
{
    string tempSubjectId;
    int subjectId;

    //temp variable to then use to convert them to int.
    string codeTemp, abilityTemp, consistencyTemp;

    std::ifstream studentFile(fileName);

    //A new student data member is created
    Student newStudent;


    if(studentFile.is_open() && studentFile.good())
    {
        cout << " " << endl;
        cout << "---Reading from Students---" << endl;
        while(getline(studentFile,newStudent.name, ','))
        {

            //we go get each value which is delimited by a comma and one by a colon
            //getline(studentFile, newStudent.name, ',');

            //To convert the strings to an int, the string is given to a temporary variable
            //Then the temporary variable is parsed to an int using stoi and the code datamember from the struct is assign to that new int
            getline(studentFile, codeTemp, ',');
            newStudent.code = stoi(codeTemp);

            getline(studentFile, abilityTemp, ',');
            newStudent.ability = stoi(abilityTemp);

            getline(studentFile, consistencyTemp, ',');
            newStudent.consistency = stoi(consistencyTemp);

            getline(studentFile, newStudent.programName, ':');

//want to push ints into subject list here.


            //The new struct data member is added to the vector and returned for further use.
            studentList.push_back(newStudent);
        }
        //file is then closed
        studentFile.close();```

Upvotes: 2

Views: 666

Answers (4)

DannyK
DannyK

Reputation: 1392

I am big proponent of Arash Partow's String Toolkit Library. It can actually handle reading and parsing text almost magically. You could setup a lamba that parses line by line and a great deal of other stuff based on your structure. Here is a crude example.

#include <iostream>
#include <vector>
#include <string>
#include <strtk.hpp>  // String Toolkit Library


struct Student_t
{
    string name;
    int code;
    int ability;
    int consistency;
    string programName;
    vector<int> subjectList;
};


const char *whitespace  = " \t\r\n\f";

// in case you wanted to parse on stuff with comma or whitespace
// it is just a matter of choosing what you want to parse
// by
/// const char *whitespace_and_punctuation  = " \t\r\n\f;,=";

const char *comma_and_colon = ",:";

int main()
{
   std::fstream input("file.txt", std::ios::in );
   std::string line;
   std::vector<Student_t> students;
   while(getline(input_strm, line) ) 
   {

       // clean up the string (precaution)
       strtk::remove_leading_and_trailing(whitespace, line);

       Student_t s;  // declare an instance

       // The parse function will fill in & convert the values into the variables
       // Since the last element is a vector, all the remaining integers will  
       // be converted.

       // the example does assume that a colon is used only once
       if(!strtk::parse( line, comma_and_colon,  s.name, s.code, 
                   s.ability, s.consistency, s.programName, s.subjectList ) ) {
           continue;
           // you may want to handle the parse error differently
        }

        students.push_back(s);

   }

}

I've answered a number of questions using the String Toolkit library and others have too. The library is C++, header only and fast. The Library have tons of examples, good documentation.

Upvotes: 4

Remy Lebeau
Remy Lebeau

Reputation: 598279

In your main loop, read an entire line into a std::string, then use a std::istringstream to parse each line, using an inner loop to read the subject ints, eg :

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

struct Student
{
    std::string name;
    int code;
    int ability;
    int consistency;
    std::string programName;
    std::vector<int> subjectList;
}; 

std::vector<Student> studentList;

void createStudents(std::string fileName)
{
    std::string tempLine;

    //temp variable to then use to convert them to int.
    std::string tempStr;

    std::ifstream studentFile(fileName);

    if (studentFile.is_open())
    {
        std::cout << " " << std::endl;
        std::cout << "---Reading from Students---" << std::endl;

        while (std::getline(studentFile, tempLine))
        {
            std::istringstream iss(tempLine);

            //A new student data member is created
            Student newStudent;

            //we go get each value which is delimited by a comma and one by a colon

            std::getline(iss, newStudent.name, ',');

            //To convert the strings to an int, the string is given to a temporary variable
            //Then the temporary variable is parsed to an int using stoi and the code datamember from the struct is assign to that new int
            std::getline(iss, tempStr, ',');
            newStudent.code = std::stoi(tempStr);

            std::getline(iss, tempStr, ',');
            newStudent.ability = std::stoi(tempStr);

            std::getline(iss, tempStr, ',');
            newStudent.consistency = std::stoi(tempStr);

            std::getline(iss, newStudent.programName, ':');

            // push ints into subject list
            while (std::getline(iss, tempStr, ',')) {
                newStudent.subjectList.push_back(std::stoi(tempStr));
            } 

            //The new struct data member is added to the vector and returned for further use.
            studentList.push_back(std::move(newStudent));
        }

        //file is then closed
        studentFile.close();
    }
}

Upvotes: 3

ChauhanTs
ChauhanTs

Reputation: 469

Here is modified code you can try, [not tested]

struct Student
{
    string name;
    int code;
    int ability;
    int consistency;
    string programName;
    vector<int> subjectList;
};

void createStudents(string fileName)

{
    string tempSubjectId;
    int subjectId;
//temp variable to then use to convert them to int.
string codeTemp, abilityTemp, consistencyTemp;

std::ifstream studentFile(fileName);

//A new student data member is created
Student newStudent;


if(studentFile.is_open() && studentFile.good())
{
    cout << " " << endl;
    cout << "---Reading from Students---" << endl;
    while(getline(studentFile,newStudent.name, ','))
    {

        //we go get each value which is delimited by a comma and one by a colon
        //getline(studentFile, newStudent.name, ',');

        //To convert the strings to an int, the string is given to a temporary variable
        //Then the temporary variable is parsed to an int using stoi and the code datamember from the struct is assign to that new int
        getline(studentFile, codeTemp, ',');
        newStudent.code = stoi(codeTemp);

        getline(studentFile, abilityTemp, ',');
        newStudent.ability = stoi(abilityTemp);

        getline(studentFile, consistencyTemp, ',');
        newStudent.consistency = stoi(consistencyTemp);

        getline(studentFile, newStudent.programName, ':')
        std::string line;
        std::vector <int> arr;
        getline(studentFile, line);
        boost::split(arr, line, [](char c) {return (c==',');})

        newStudent.subjectList = arr;


    }
    //file is then closed
    studentFile.close();```

Upvotes: 0

john
john

Reputation: 88027

You need another loop, and an string stream to read from, like this

#include <sstream>

string subjectList;
getline(studentFile, subjectList); // read all the subjects
istringstream iss(subjectList);     // put into a string stream for parsing
string subject;
while (getline(iss, subject, ','))  // read from string stream, spliting on commas
{
    newStudent.subjectList.push_back(stoi(subject)); // add to subject list
}

Untested code.

Upvotes: 0

Related Questions