files in c++

hey guys i have 2 files ( students.dat and grades.dat )
students.dat : ID number followed by a first name then last name ( first and last name are 20 charac max ) .
grades.dat : ID number followed by 3 grades .
the question is :Read the data from “grades.dat” file and print on the screen the Student’s ID and Students’s Name (first name, last name) and the total grade.
- i have to find the firstname and the lastname in the “student.dat” file.

how should i search the first name and last name in students.dat to it's corresponding ID number
Student Data:
1 John Smith
2 Jane Doe
3 AN Other
4 Some Guy
5 Some Gal

Grades Data:
1 34 35 36
3 37 38 39
5 40 41 42
7 43 44 45

Program
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <vector>
#include <numeric>

struct Student
{
    int m_ID;
    std::string m_firstName;
    std::string m_lastName;
    std::vector<double> m_grades {3, 0};
    bool m_match = false;
};
std::ostream& operator << (std::ostream& os, const Student& s)
{
    os << s.m_ID << " " << s.m_firstName << " " << s.m_lastName << " " ;
    if (s.m_match == false)
    {
        os << "No grade records found for this student ID \n";
    }
    else
    {
        os << "Total grade: " << std::accumulate(s.m_grades.begin(), s.m_grades.end(), 0) << '\n';
    }
    return os;
}
struct Grade
{
    int m_ID;
    std::vector<double> m_grades;
};

int main()
{
    std::ifstream studentsFile("D:\\studentsD.dat");
    std::vector <Student> studentsVec;
    if(studentsFile)
    {
        std::string line;
        while (getline(studentsFile, line))
        {
            Student tmpStudent;
            std::stringstream stream(line);
            stream >> tmpStudent.m_ID >> tmpStudent.m_firstName >> tmpStudent.m_lastName;
            if(studentsFile)
            {
                studentsVec.push_back(std::move(tmpStudent));
            }
        }
    }
    std::ifstream gradesFile("D:\\gradesD.dat");
    std::vector <Grade> gradesVec;
    if(gradesFile)
    {
        std::string line;
        while (getline(gradesFile, line))
        {
            Grade tmpGrade;
            std::stringstream stream(line);
            stream >> tmpGrade.m_ID;
            double tmp;
            while (stream >> tmp)
            {
                tmpGrade.m_grades.push_back(std::move(tmp));
            }
            if(gradesFile)
            {
                gradesVec.push_back(std::move(tmpGrade));
            }
        }
    }
    for (auto itrS = studentsVec.begin(); itrS != studentsVec.end(); ++itrS)
    {
        for (auto itrG = gradesVec.begin(); itrG != gradesVec.end(); ++itrG)
        {
            if ((*itrS).m_ID == (*itrG).m_ID)
            {
                (*itrS).m_grades = (*itrG).m_grades;
                (*itrS).m_match = true;
            }
        }
    }
    for (auto& elem : studentsVec)
    {
        std::cout << elem ;
    }
}
Last edited on
Hey Julien123, if you want a simplified version (all in main(), using char array), here's one way:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
#include<fstream>
using namespace std;
int main()
{
    ifstream file1("students.dat");
    ifstream file2("grades.dat");
    char name[21]; //21 because of the null character and the end
    int ID, grade1, grade2. grade3;
    while (!file1.eof())
    {
        file1>>ID;
        file1.get();
        file1.get(name, 20, '\n');
        file2>>ID;
        file2>>grade1;
        file2>>grade2;
        file2>>grade3;
        cout<<"ID: "<<ID<<"; Name: "<<name<<"; Total Grade: "<<grade1+grade2+grade3;
    }
    cin.get();
    cin.get(); //just to pause the program at the end
}
@Troaat The version you posted doesn't work, and is not helpful. It is in any case bad practice to use .eof() in a while loop.

http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong/5605159#5605159
@Chervil

The version you posted doesn't work, and is not helpful. It is in any case bad practice to use .eof() in a while loop.

I hadn't known it was bad practice. My teacher showed the class this, and I didn't think there was something wrong in such application, like the one above. I've been mainly using other loop-terminating conditions anyway.
But could you show me what was wrong about the program? I haven't tested it so I don't really know what it is exactly that I've bugged in that program.
Troaat: it's good you made a positive contribution, some feedback on your program in addition to what Chervil already mentioned:

- try and avoid using namespace std -
http://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice
- prefer std::string over char and std::vector over C-style arrays
- the period after grade2 (line 9) should be replaced by a comma
- as a style issue I'd also suggest putting some spaces around the insertion << and extraction >> operators for better readability
- and the main point is that since the data is coming from 2 different files you need to make sure that ID in file#1 matches ID in file#2. for e.g. if you run your program against the 2 sample files I provided you'll find that data from file#2 is read against entries from file#1 indiscriminately, without first checking that the ID's match
> how should i search the first name and last name in students.dat to it's corresponding ID number

Consider using std::map<> (or std::unordered_map<>) with the key being the ID and the mapped value being the pair of names.
http://en.cppreference.com/w/cpp/container/map

For instance:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <string>
#include <map>
#include <fstream>

struct student
{
    std::string first_name = "Anonymous" ;
    std::string last_name = "noname" ;
};

std::map< int, student > get_names( std::string names_file = "student.dat" )
{
    std::map< int, student > students ;

    std::ifstream file(names_file) ;

    int id ;
    std::string first_name ;
    std::string last_name ;
    while( file >> id >> first_name >> last_name ) students[id] = { first_name, last_name } ;

    return students ;
}

void print_grades( const std::map< int, student >& students, std::string grades_file = "grades.dat" )
{
    std::ifstream file(grades_file) ;

    int id ;
    int grade[3] ;

    while( file >> id >> grade[0] >> grade[1] >> grade[2] )
    {
        const auto iter = students.find(id) ;
        const student& s = iter != students.end() ? iter->second : student{} ;

        std::cout << "id: " << id << "  " << s.last_name << ", " << s.first_name << "  "
                  << grade[0] << ' ' << grade[1] << ' ' << grade[2] << "  average: "
                  << double( grade[0] + grade[1] + grade[2] ) / 3 << '\n' ;
    }
}

int main()
{
    const std::string names_file = "student.dat" ;
    const std::string grades_file = "grades.dat" ;
    print_grades( get_names(names_file), grades_file ) ;
}

http://coliru.stacked-crooked.com/a/eefabf2b45987225
@gunnerfunner

- try and avoid using namespace std -

I know, I don't really use it anymore, but I thought that if someone read it, there was a chance of that person being like I was a few months ago. I hadn't understood why people kept writing "std::cout" and such, which was confusing for me.

- prefer std::string over char and std::vector over C-style arrays

Since Julien123 stated that he needed names that were at most 20 characters long, I supposed it needed to be like our teacher taught and requested us a whole year. Believe me, I don't ever use char arrays anymore, and I prefer the string all the time.

- the period after grade2 (line 9) should be replaced by a comma

Yea, I haven't noticed that one. Since it was a quick-writing, I didn't really look upon the code a second time.

- as a style issue I'd also suggest putting some spaces around the insertion << and extraction >> operators for better readability

As for this one, you're right for some or most people, but I, for one, can't see the operator in some cases, and since nobody told me in the beginning that I should leave spaces, I just went with this one.
On the bright side, I'm almost over with cout, therefore it won't be a main concern. Readibility will not be required.

- and the main point is that since the data is coming from 2 different files you need to make sure that ID in file#1 matches ID in file#2. for e.g. if you run your program against the 2 sample files I provided you'll find that data from file#2 is read against entries from file#1 indiscriminately, without first checking that the ID's match

I thought that it was the same order for both.
From what I thought, it was a good match with what I would do when writing information in files: first student, some data in first file, some data in second file. Since (if) it's not how I thought, then of course it's on me, because I am not a fan of seeing such meanings. I just improvise and create programs that don't have the requirement written that way.
JLBorges: for the 2 sample files I'd posted in my original reply your program outputs:
1
2
3
4
id: 1  Smith, John  34 35 36  average: 35
id: 3  Other, AN  37 38 39  average: 38
id: 5  Gal, Some  40 41 42  average: 41
id: 7  noname, Anonymous  43 44 45  average: 44

Also line 27 - it's C++17 I suppose since C++14 compiler reported:
 
no match for 'operator=' (operand types are 'std::map<int, student>::mapped_type {aka student}' and '<brace-enclosed initializer list>')|
On the bright side, I'm almost over with cout, therefore it won't be a main concern. Readibility will not be required.

What? Readability should always be a major concern. If the program can't easily be read then it will be virtually impossible to debug and troubleshoot.

@jlb
What I meant is the readability for "<<" operator, since cout is often used in the first years of programming. I am well aware of what coding with no boundaries leads to. I, myself, would use 1 letter to declare a variable, and now when I want to look inside the source codes from two years ago I can't figure out simple stuff I wrote down.
What I meant is the readability for "<<" operator, since cout is often used in the first years of programming.

Operator<< is used with more than just console input and output. It is used in many many other areas as well since all "streams" have the ability to use these operators.

And remember when posting code to "help" others you should be very deliberate in how your code is presented. Show well formatted code to stress how important proper code formatting is to reading and troubleshooting programs. For an example look at the code JLBorges posted above to see good example of well formatted code that is easy to read and how easy it is to follow the program logic.



Last edited on
closed account (48T7M4Gy)
And remember when posting code to "help" others you should be very deliberate in how your code is presented.


How rude is that! Good on you Troatt for contributing positively as gunner wrote.
@kemort
Eh, don't mind what @jlb said. After all, I'm learning from mistakes, such as not testing a code before giving it to someone. It's as if I gave a book manuscript to someone, but it had no action even though the book was an action story.
Topic archived. No new replies allowed.