Despicable me Despicable me - 2 months ago 14
C++ Question

C++: reading from txt file with multiple delimeter

Is there any way to read from file containing student records but delimited by different characters? My text is as below:

125 {John_BROWN_Biology} {100_81}
245 {Amanda_SMITH_Chemistry} {83_74}
436 {Michael_CLARK_Calculus} {35_48}

I have to store the in separate arrays so that I can calculate their average and put in ascending order.
I have written the code in C++ in this way:

int rank;
char st_info[SIZE];
int grades[SIZE];
bool success1 = false;
bool success2 = false;
bool success3 = false;

ifstream inFile;"records.txt");
string line;
int i=0, j=0;
while (!inFile.getline(rank, 30, '{').eof){
inFile.getline(st_info, SIZE, '_');
inFile.getline(words_array, SIZE, '_');
while (!success1)
getline(inFile,Line,'{'); // get values for info array
stringstream iss1;
iss1 << Line; //Put the string into a stream
iss1 >> rank; //To send data as an int.
cout << "STUDENT NUMBER:" << rank << " ";
while (!success2){
// for the info in {} part
stringstream iss;
iss << Line;
iss >> st_info[i];
cout <<"STUDENT INFO:" << st_info[i] << " ";
if (getline(inFile,Line,'}')){
stringstream iss2;
iss2 << Line;
iss2 >> st_info[i];
cout << st_info[i] << " ";
success2 = true;
stringstream iss3;
iss3 << Line;
iss3 >> grades[j];
cout << "GRADES: "<< grades[i] << " ";
while (!success3){
stringstream iss4;
iss4 << Line;
iss4 >> grades[j];
cout << grades[i] << " ";
if (getline(inFile,Line,'}')){
stringstream iss5;
iss5 << Line;
iss5 >> grades[j];
cout << grades[i] << " ";
success3 = true;

However, as an output I am getting:

1 J 100
2 A 100
4 M 100

Tried to fix, but it get worse. Anybody who can help? Thanks in advance.


There are several issues with your approach and your code:

  • istream::getline() can't read integers. It can only read into an array of char array.
  • eof is a function and not a property
  • the way you mix << >> to parse data with stringstream is not optimal.
  • with >>st_info[i] you extract a single char from the stringstream, that will overwrite existing information.
  • and certainly other problems.

I propose you therefore to use the following skeleton, that reads the file line by line, and parses each line separately using a stringstream. Note that I only use the non-member variant of getline() to read strings instead of arrays of char (this frees me from thinking of buffer overflows):

char delim;  
int rank;
string names, grades;
string line;

while (getline(inFile, line))   // read line by line 
    stringstream sst{line};     // then parse the line using a string stream

    sst>>rank;                  // read an integer
    sst>>delim;                 // skip white and read a single char 
    if (delim!='{') {
        cout<<"Error on line: { expected for name instead of "<<delim<<endl; 
        continue;   // next line, please !! 
    getline(sst,names, '}');  // TO DO: error handling
    if (delim!='{') {
        cout<<"Error on line: { expected for grades instead of "<<delim<<endl; 
        continue;   // next line, please !! 
    getline(sst,grades, '}');  // TO DO: additional error handling

    cout << rank<<" "<<names<<" "<<grades<<endl; // temporary:  

    // TO DO: parse names and grades by using further stringstreams 

Online demo

Note that I used a simple parsing approach: read a char and check it matches the expected opening character, and use getline() to read until the closing character (the latter being consumed but excluded from the string). This doesn't allow for nested {...} in your format.