Help: Reading file into linked list.

Started by
2 comments, last by Zahlman 17 years, 10 months ago
Hi All, I'm wondering if anyone could help me out with an issue that's been plaguing me. In a nutshell, I am trying to read a file that holds triangle/vertices information and load it into a linked list. The triangle file format is arranged as follows: x1 y1 z1 x2 y2 z2 x3 y3 z3 color Here's an example of a triangle file: 0.72 0.735 -0.665 0.593832 0.72179 -0.614606 0.623435 0.72179 -0.571884 0xFFFFFF 0.72 0.735 -0.665 0.583437 0.72179 -0.665 0.593832 0.72179 -0.614606 0xFFFFFF 0.72 0.735 -0.665 0.593832 0.72179 -0.715394 0.583437 0.72179 -0.665 0xFFFFFF 0.72 0.735 -0.665 0.623435 0.72179 -0.758116 0.593832 0.72179 -0.715394 0xFFFFFF I've written some code that apparently works, in c++, using ifstream. The code reads the file, loads the linked list, displays the linked list and finally clears the list. But, I've noticed that whenever the list is displayed, there is always an extra line with the values 0 0 0 0 0 0 , etc. From examining the code, I *think* the problem lies in my " while(!tri_file.eof())" loop. I suspect that the eof is occuring on the last loop, but we end up entering the while loop one more time. Can anyone shed some light on how I might fix this bug?? Thank you all very much!!! :) --Mike //******** Code below ********************8 // g++ under linux 2.4 #include <iostream> #include <string> #include <fstream> using namespace std; struct VECTOR { double x1,y1,z1; double x2,y2,z2; double x3,y3,z3; string color; VECTOR* next; }; VECTOR* get_vectors(); void print_vectors( const VECTOR* ptr ); void free_list( const VECTOR* ptr ); int main() { VECTOR* start; start = get_vectors(); print_vectors( start ); free_list( start ); return 0; } VECTOR* get_vectors() { VECTOR *current, *first; ifstream tri_file("sphere.tri"); if (tri_file.is_open()) { current = first = new VECTOR; tri_file >> current -> x1 >> current->y1 >> current -> z1 >> current -> x2 >> current->y2 >> current -> z2 >> current -> x3 >> current->y3 >> current -> z3 >> current -> color; //Ignore color while(!tri_file.eof()) { current = current -> next = new VECTOR; tri_file >> current -> x1 >> current->y1 >> current -> z1 >> current -> x2 >> current->y2 >> current -> z2 >> current -> x3 >> current->y3 >> current -> z3 >> current -> color; //Ignore color } current -> next = 0; //Create sentinal for printing.... } tri_file.close(); return first; } void print_vectors( const VECTOR* ptr ) { int count = 1; cout << "\n\n\n"; while ( ptr != 0 ) { cout << "Vector number " << count++ << " is " << ptr -> x1 << ' ' << ptr -> y1 << ' ' << ptr -> z1 << '\t' << ptr -> x2 << ' ' << ptr -> y2 << ' ' << ptr -> z2 << '\t' << ptr -> x3 << ' ' << ptr -> y3 << ' ' << ptr -> z3 << '\t' << '\n'; ptr = ptr -> next; } } void free_list( const VECTOR* ptr ) { const VECTOR* temp_ptr; while ( ptr != 0 ) { temp_ptr = ptr -> next; delete ptr; ptr = temp_ptr; } } //************** END ************************
Advertisement
Adding a >> std::ws to the end of your reading code should trigger an eof at the appropriate time.

You are aware that the SC++L contains a list class, right?

Σnigma
Σnigma,

WOW!! That was the fastest fix ever!! Thank you very much, now things are looking good!

In regards to the list class, yeah, I'm aware of it.... I haven't done C++/oop in over a year, so I'm trying to *relearn* everything. (including basic datastructures...)

Thanks again for the quick reply and such!!!

--Mike
Code should go in either [code][/code] (short) or [source][/source] (long) tags.

And naturally, with std::list we can do this more easily. I'd also like to take the opportunity to show a couple of other idioms... (btw, I think std::vector is likely to be a more appropriate container here. Although C++ is case sensitive, and you could also remove the using-declaration and qualify std::vector explicitly, I have renamed your struct for clarity.

#include <iostream>#include <string>#include <fstream>#include <list>using namespace std;struct triangle {  double x1,y1,z1;  double x2,y2,z2;  double x3,y3,z3;  string color;};typedef vector<triangle> triangles;// The standard library container will do the memory management for you.// Yes, it's good to know how to do this stuff, but it's better to practice that// by designing your *own* classes for *unique* purpose. You can also try// learning by *reading* your compiler's std library implementations (but be// warned that the variable names usually suck).// I prefer to put functions in order - declaration before use - within the// implementation file. Of course, in a header you still need to declare// all "public" functions anyway, but by doing it this way, you save typing,// and also highlight circular dependencies in the functions (by the fact// that you will be *forced* to declare one in order to compile).// As a helper function for get_tris(), I implement an operator overload that// reads a single triangle.istream& operator>>(istream& is, triangle& tri) {  return is >> tri.x1 >> tri.y1 >> tri.z1            >> tri.x2 >> tri.y2 >> tri.z2            >> tri.x3 >> tri.y3 >> tri.z3            >> tri.color >> ws;}triangles get_tris() {  triangles result;  triangle current;  ifstream tri_file("sphere.tri");  while (tri_file >> current) {    // That will implicitly check if the file is open, because the first read    // would fail otherwise.    result.push_back(current);  }  // You can actually do better, using std::copy from the <algorithm> header; \  // I will show that upon request.  return result;}// Passing by reference allows you the functionality acheived by "passing by// pointer" in C, without all the manual referencing and dereferencing (either// at the call site or in the implementation).void print_tris(const triangles& v) {  cout << "\n\n\n";  int count = 0;  for (triangles::iterator it = v.begin(); it != v.end(); ++it) {    // The iterator provides a pointer-like interface, accessing elements of    // the vector in turn.    cout << "Vector number " << ++count << " is "         << it->x1 << ' ' << it->y1 << ' ' << it->z1 << '\t'         << it->x2 << ' ' << it->y2 << ' ' << it->z2 << '\t'         << it->x3 << ' ' << it->y3 << ' ' << it->z3 << '\t'         << '\n';  }}// You can actually "do better" with the algorithm header, again, although for // a code snippet this short the benefit isn't really realized. The idea is// to create an operator overload to output a single triangle, and then use the// same approach as for the reading, specifying 'cout' as an output stream.// However, that requires that you treat this as the definitive way of// "outputting a triangle", which may not be what you want (e.g. if you later// implement saving of triangles that were modified in your program). In that// case, you could make use of std::for_each() instead.int main() {  // You should initialize variables to their first value, whenever that value  // is determined by a known expression (i.e., isn't set by using the variable  // as an out-parameter).  triangles start = get_tris();  print_tris(start);  // But note that you don't even need a variable here, since the memory  // management disappeared and thus there is only one call upon the vector.  // You could equivalently write: print_tris(get_tris());  // Anyway, as a special case, you don't need to return 0 from main(). This is  // defined behaviour in the standard - falling off the end of main implicitly  // returns 0, although it wouldn't for other functions. Anyway, my style  // generally favours omitting things that do no work and don't imply anything  // that couldn't easily be inferred, so I never write it myself.}

This topic is closed to new replies.

Advertisement