I have a text file that stores the index, student name and student ID and I am trying to read them into an array of integers index
, arrays of strings studentName
and studentID
. I'm having problems storing the student's names because they could be more than a single word. I could separate the items in the text file by commas and use getline
but that would mean the index
array will have to be a string type. Is there a workaround for this without changing the original text file?
Original file:
1 James Smith E2831
2 Mohammad bin Rahman M3814
3 MJ J4790
const int SIZE = 3;
int index[SIZE];
string studentName[SIZE], studentID[SIZE];
fstream infile("students.txt");
if(infile.is_open()){
int i = 0;
while(i < 3){
infile >> index[i] >> studentName[i] >> studentID[i];
i ;
}
}
Changed file:
1,James Smith,E2831
2,Mohammad bin Rahman,M3814
3,MJ,J4790
const int SIZE = 3;
string index[SIZE];
string studentName[SIZE], studentID[SIZE];
fstream infile("students.txt");
if(infile.is_open()){
int i = 0;
while(i < 3){
getline(infile, index[i],','); //index array is string
getline(infile, studentName[i],',');
getline(infile, studentID[i],'\n');
i ;
}
}
CodePudding user response:
It's an error to read one line into one student property with the given input format. You need to read one line and then split the information in this line into the 3 properties.
std::stoi
can be used to convert to convert the first part of the line read to an int
. Futhermore it's simpler to handle the data, if you create a custom type storing all 3 properties of a student instead of storing the information in 3 arrays.
Note that the following code requires the addition of logic to skip whitespace directly after (or perhaps even before) the ','
chars. Currently it simply includes the whitespace in the name/id. I'll leave that task to you.
struct Student
{
int m_index;
std::string m_name;
std::string m_id;
};
std::vector<Student> readStudents(std::istream& input)
{
std::vector<Student> result;
std::string line;
while (std::getline(input, line))
{
size_t endIndex = 0;
auto index = std::stoi(line, &endIndex);
if (line[endIndex] != ',')
{
throw std::runtime_error("invalid input formatting");
}
endIndex;
auto endName = line.find(',', endIndex);
if (endName == std::string::npos)
{
throw std::runtime_error("invalid input formatting");
}
result.push_back(Student{ index, line.substr(endIndex, endName - endIndex), line.substr(endName 1) });
}
return result;
}
int main() {
std::istringstream ss(
"1,James Smith,E2831\n"
"2, Mohammad bin Rahman, M3814\n"
"3, MJ, J4790\n");
auto students = readStudents(ss);
for (auto& student : students)
{
std::cout << "Index=" << student.m_index << "; Name=" << student.m_name << ";Id=" << student.m_id << '\n';
}
}
CodePudding user response:
There are so many possible solutions that it is hard to tell, what should be used. Depends a little bit on your personal style and how good you know the language.
In nearly all solutions you would (for safety reasons) read a complete line with std::getline
then put either split the line manually or use a std::istringstream
for further extraction.
A csv input would be preferred, because it is more clear what belongs together.
So, what are the main possibilities? First the space separated names
- You could use a
std::regex
and then search or match for example "(\d) ([\w ] ) (\w )" - You cou create substrings, by searching the first space from the right side of the string, which would be the "studentID", then the getting the rest is simple
- You could use a while loop and read all parts of the string and put it in a
std::vector
. The first element in thestd::vector
is then the index, the last the ID and the rest would be the name. - You could parse the string with formatted input functions. First read the index as number, then the rest as stringm the split of the last part and get the id.
And many more
For csv, you could use
- the
std::sregex_token_iterator
looking for the comma as separator - Use also a
std::vector
and later pick the needed values. - Use a mixture of formatted and unformatted input
Example:
std::getline(std::getline(infile >> index >> Comma >> std::ws, name, ','), id);
It is up to you, what you would like to implement.
Write your preference in the comment, then I will add some code.