Reading from a txt file to linked list

May 16, 2020 at 8:42am
Hi,
I just got started with Linked lists and i am trying to read from a txt file to linked list. This is my first attempt. I have no idea how to achieve this. Any help will be greatly appreciated. Cheers.
Last edited on May 21, 2020 at 7:18am
May 16, 2020 at 9:19am
There are two things I’d like you to consider:
1 make an append function to append vals to lists
and
2 the use of new instead of malloc

1) Making an append function will allow you to split up some of the work and allow you to see where you are having problems better, because things achieving different goals will be in different places. Also, using a class or struct in C++ introduces that concept of abstraction and encapsulation, both extremely important and helpful concepts that I suggest you look up if you haven’t learned them yet.

2) Using new is just easier. The only time maybe to use malloc in C++ code is for performance probably.
May 16, 2020 at 12:45pm
You seem to have some confusion about where to store the data. You allocate an array of 100 items in main and you also allocate space on the heap for each item with malloc in read_file(). Since this is a linked list, you should put them all on the heap.

Have you heard the phrase "garbage in / garbage out?" It means if you start with junk in your input, you'll always have junk in the output. In this program, you want to be 100% certain that you're reading and writing student_tag records correctly before you start dealing with the list.

I strongly urge you to start by writing read() and write() methods in student_tag to read and write one student:
1
2
bool student_tag::read(istream &is);
bool student_tag::write(ostream &os);


Then, temporarily change main to this:
1
2
3
4
5
6
7
int main()
{
    student_tag student;
    while (student.read(cin)) {
        student.write(cout);
    }
}


Work on this until you can read and write the whole input file.

Once you have these methods working, you can use them in read_file() and display(). Something like:

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
student_tag *read_file() //readfile function
{
    int count;
    double total = 0;
    string filename = "test.txt";
    ifstream inFile;

    inFile.open(filename.c_str());

    if (inFile.fail()) // if file doesnt open successfully
    {
        cout << "The File was not opened successfully\n";
        cout << "Please check that the file currently exists\n";
        return nullptr;
    }
    int i = 0;
    student_tag *tail=nullptr;
    hptr = nullptr;
    student_tag *nptr = new student_tag;
    while (nptr->read(inFile)) // until the end of the file
    {
        if (tail) {
           tail->next = nptr;
       } else {
           hptr = nptr;
       }
       tail = nptr;
       nptr = new student_tag;  // create space for the next one
   }
   // You created space for the one that failed to read. Delete it
   delete nptr;
   inFile.close();  //close file
   return hptr;
}


See how nicely this divides the code? Now read_file() is almost entirely about adding items to the list while student_tag::read() is all about reading an item from a stream.
May 16, 2020 at 1:48pm
Hello dipeshgoyal44,

Debugging the program should be much the same as when you wrote the program. Done in small steps.

As dhayden said first get the program to read the file. You could try this to get started with:
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
int tempReadFile(int& array_size)
{
	std::string name;
	std::string id;
	std::string courseName;

	int units{};

	const std::string inFileName{ "test.txt" };

	std::ifstream inFile(inFileName);

	if (!inFile)
	{
		std::cout << "\n File " << std::quoted(inFileName) << " did not open" << std::endl;
		//std::this_thread::sleep_for(std::chrono::seconds(3));  // <--- Needs header files chrono" and "thread". Optional.
		return 1;
	}

	inFile >> name;
	inFile >> id;
	inFile >> courseName;
	inFile >> units;

	std::cout
		<< "\n Name: " << name
		<< "\n ID: " << id
		<< "\n Course Name: " << courseName
		<< "\n Units: " << units;

	return 0;
}


From the input file of:

Andy Smithe
123456
Computer Science 101
5



I get this:

 Name: Andy
 ID: Smithe
 Course Name: 123456
 Units: 0



If I knew what your input file looks like this might be different. Post the input file, if large 3 or 4 records will work, so that everyone will know what you are working with and can use the same information when testing.

You can add variables to read a complete record and print it out.

This is a quick to get the reading of the file correct then you can adjust the regular read function to match Then move on to creating the linked list.

Andy
May 16, 2020 at 2:20pm
Thanks alot guys.

here is the input file:


Zen
1101825
Computer
4
88
98
52
95
Peter
112152
Electrical
3
67
98
59
Mary
1201925
Mechanical
4
78
55
79
75
Sherin
1201925
Civil
4
69
53
78
88
May 16, 2020 at 2:26pm
I did the same program with array of structure before and the readfile function worked fine.
here is the function that i used.


Last edited on May 18, 2020 at 4:48am
May 16, 2020 at 7:20pm
But that's reading into an array. You're reading into a linked list, which is a different beast.
May 16, 2020 at 10:29pm
Hello dipeshgoyal44,

When I finally figured out how to read the file your code started to make some sense.

You should delete the array you define in "main" and not pass it in the function call and remove it from the prototype and function definition. The program is about linked lists not arrays.

I finally realized that "hptr" is the "head". You can use "hptr" if you want, but put a comment after it to say it is the head. It makes understanding what it is easier.

Next you set "hptr" to NULL when defined. "nullptr", from C++11 on, would be the better choice, but you never give "hptr" a proper value, so your linked list has nowhere to start. Refer to dhayden's earlier code for some tips.

The line: inFile.open(filename.c_str());. Are you saying that the compiler is not using the C++11 standards or is it that you are being taught the old way of doing this? From C++11 on you can use ifstream inFile(filename);. Using a std::string works just fine. Also you can do this in one line instead of two.

In the read function taking out the array will make your code much easier to work with.

Andy
May 17, 2020 at 8:11am
That's what i am not sure about. how do i read from file into a linked list? if it is in a function what do i return? I just can't seem to understand how data insertion into linked list works from a file.
May 17, 2020 at 8:22am
1
2
3
std::string value;
filestream >> value;
/* append a node containing value to you list */
May 17, 2020 at 12:31pm
This is what i have so far.
Last edited on May 21, 2020 at 7:18am
May 17, 2020 at 1:24pm
Ok i figured out how to store data into the linked list from file. its really really messy but i have this assignment due tomorrow and i need it to work. So now i am storing everything in the read function. i wanted to use the function @dhayden wrote but i have no idea how to implement it. I can print the linked list in the read function but the assignment asks to have a separate display function. i am not sure how to display it now. Can someone have a look? Thanks so much guys. i really appreciate all your help.
Last edited on May 21, 2020 at 7:19am
May 17, 2020 at 2:03pm
You're display function looks good, but the read function is still not right. Since you're making a good effort, I'm going to give it to you. Look this over carefully and make sure you understand it.

There's still one thing missing: the average. You could either:
- add code to read() to compute the average once you've read the record, or
- remove avg from course_tag completely and compute the average in display() when you need it.

In general, it's bad to have redundant data and the avg is redundant. The problem is that you might change one of the grades and then forget to update avg. So I'd opt for the second method: remove avg from course_tag and compute the average in display
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>

using namespace std;

struct person_tag
{
    string name;
    string id;
};

struct course_tag
{
    string course_name;
    int no_of_units;
    int marks[4];
    double avg;
};

struct student_tag
{
    person_tag student_info;
    course_tag course_info;
    student_tag *next;
};

typedef struct student_tag Student_Tag;
typedef Student_Tag *Student_TagPtr;

Student_Tag *hptr;
Student_Tag *cptr;
Student_Tag *nptr;

//function protypes
void menu();
void read();
void display_students();

void
read()				//readfile function
{
    hptr = nptr = NULL;				 //intialized to null
    string filename = "students.txt";
    ifstream inFile(filename);

    if (inFile.fail())				 // if file doesnt open successfully
    {
	cout << "The File was not opened successfully\n";
	cout << "Please check that the file currently exists\n";
	exit(1);
    }
    while (inFile.peek() != EOF)		 // until the end of the file
    {
	cptr = new Student_Tag;
	// Read the data into the new item
	inFile >> cptr->student_info.name;
	inFile >> cptr->student_info.id;
	inFile >> cptr->course_info.course_name;
	inFile >> cptr->course_info.no_of_units;
	for (int j = 0; j < cptr->course_info.no_of_units; j++) {
	    inFile >> cptr->course_info.marks[j];
	}
	if (hptr == NULL) {
	    // First item in the list. Point hptr at it
	    hptr = cptr;
	} else {
	    // Not the first item, append it to the tail.
	    nptr->next = cptr;
	}
	nptr = cptr;		// Move the tail pointer
    }
    cptr->next = NULL;		// set the last items next pointer to NULL
    inFile.close();				 //close file
}

void
display()
{
    student_tag *ptr;
    ptr = hptr;
    while (ptr != NULL) {
	cout << "The Student Name: " << ptr->student_info.name << "  \n";
	cout << "The Student ID: " << ptr->student_info.id << "  \n";
	cout << "The course name: " << ptr->course_info.course_name << "  \n";
	cout << "Number of units " << ptr->course_info.no_of_units << "  \n";
	cout << "Marks recieved: \n";
	for (int j = 0; j < ptr->course_info.no_of_units; j++) {
	    cout << ptr->course_info.marks[j] << "  \n";
	}
	cout << "The marks average is : " << ptr->course_info.avg << "  \n";
	ptr = ptr->next;
    }
}

int
main()
{
    read();
    cout << "The linked list is: ";
    display();
    return 0;
}
May 17, 2020 at 2:34pm
hey @dhayden

Thanks so much for this. it looks much cleaner. Though when i compile it, it is printing an empty record at the end. not sure why.
May 17, 2020 at 3:55pm
Hello dipeshgoyal44,

That could be because of while (inFile.peek() != EOF) // until the end of the file. May not always work. Plus I found an extra space at the end of the input input file.

Removing the space mace it work correctly and find the (eof) marker at the proper time.

Andy
May 17, 2020 at 3:59pm
I checked the input file but it looks fine. Might be something else.
May 17, 2020 at 4:24pm
Hello dipeshgoyal44,

Looks can be deceiving.

Further investigation found that the last number (88) MUST NOT have anything following it not even a new line.

Andy
May 17, 2020 at 8:20pm
As Andy pointed out, the problem is that your code assumes that the data ends immediately after the last record. A single space or new line will throw it off.

That's actually one reason that I suggested the read() and write() methods. Read() could return success when it read the entire record. That way an error anywhere along the way, even in the middle of a record, would be caught.

Here is a version with this logic added to the existing structure. After attempting to read a record, I check to see if the stream failed and exit if so.

BTW this demonstrates something that many programs don't understand: sometimes it makes sense to put the loop exit condition in the middle of the loop.
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>

using namespace std;

struct person_tag
{
    string name;
    string id;
};

struct course_tag
{
    string course_name;
    int no_of_units;
    int marks[4];
    double avg;
};

struct student_tag
{
    person_tag student_info;
    course_tag course_info;
    student_tag *next;
};

typedef struct student_tag Student_Tag;
typedef Student_Tag *Student_TagPtr;

Student_Tag *hptr;
Student_Tag *cptr;
Student_Tag *nptr;

//function protypes
void menu();
void read();
void display_students();

void
read()				//readfile function
{
    hptr = nptr = NULL;				 //intialized to null
    string filename = "students.txt";
    ifstream inFile(filename);

    if (inFile.fail())				 // if file doesnt open successfully
    {
	cout << "The File was not opened successfully\n";
	cout << "Please check that the file currently exists\n";
	exit(1);
    }
    while (true)		 // until the end of the file
    {
	cptr = new Student_Tag;
	cptr->next = NULL;
	// Read the data into the new item
	inFile >> cptr->student_info.name;
	inFile >> cptr->student_info.id;
	inFile >> cptr->course_info.course_name;
	inFile >> cptr->course_info.no_of_units;
	for (int j = 0; j < cptr->course_info.no_of_units; j++) {
	    inFile >> cptr->course_info.marks[j];
	}

	// Did you read it okay?
	if (inFile.fail()) {
	    delete cptr;	// don't need it
	    break;
	}

	// Okay, you read it. Add to the list
	if (hptr == NULL) {
	    // First item in the list. Point hptr at it
	    hptr = cptr;
	} else {
	    // Not the first item, append it to the tail.
	    nptr->next = cptr;
	}
	nptr = cptr;		// Move the tail pointer
    }
    inFile.close();				 //close file
}

void
display()
{
    student_tag *ptr;
    ptr = hptr;
    while (ptr != NULL) {
	cout << "The Student Name: " << ptr->student_info.name << "  \n";
	cout << "The Student ID: " << ptr->student_info.id << "  \n";
	cout << "The course name: " << ptr->course_info.course_name << "  \n";
	cout << "Number of units " << ptr->course_info.no_of_units << "  \n";
	cout << "Marks recieved: \n";
	for (int j = 0; j < ptr->course_info.no_of_units; j++) {
	    cout << ptr->course_info.marks[j] << "  \n";
	}
	cout << "The marks average is : " << ptr->course_info.avg << "  \n";
	ptr = ptr->next;
    }
}

int
main()
{
    read();
    cout << "The linked list is: ";
    display();
    return 0;
}

May 18, 2020 at 4:25am
Thanks guys. everything is working fine now. Really appreciate it.
Topic archived. No new replies allowed.