Having problems with reading a file

I have made a class that works well for what I want, however I am having one little problem with it.

It basically is an array of Items (a struct containing an int and a string). It is supposed to save the file in the deconstructor and read from the file in the constructor. However many of the id's are getting messed up.

The code in question is as follows:
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
void I_List::I_getfile ()
{
   ifstream in; // open a file for reading
   in.open(I_file.c_str(), ios::in);

   if (in.is_open()) // If the file opened successfully
   {
      char line[chrsize]; // Get the first line
      in.getline(line,chrsize);

      I_top = 0; // It should already be zero, but just in case

      while (!in.eof()) // While not end of file
      {
         // Check array count and capacity and grow if needed
         if (I_top >= I_cap)
            I_grow();

         I_arr[I_top].id = (int)line[0]; // Set the ID

         char temp[chrsize]; // Char array to copy to
         int i = 0; // iterator

         while (line[i] != '\0') // while not terminating char
         {
            temp[i] = line[i+1]; // skips first char, and grabs terminator
            ++i;
         }

         I_arr[I_top].name = temp; // Assign the Name
         ++I_top; // go to next item

         in.getline(line,chrsize); // get the next line
      }

      in.close(); // close the file

      if (I_top > 1) // check if we have enough items
         I_sort(); // and sort the array
      return;
   }

   // If we got this far, file didn't open
   cout << "Error opening file: " << I_file << endl;
}

void I_List::I_putfile ()
{
   ofstream out; // open the file for writing; replace everything in it.
   out.open(I_file.c_str(), ios::out | ios::trunc);

   if (out.is_open()) // if file opened successfully
   {
      for (int i = 0; i < I_top; i++) // for each item in array
      {
         // write the id as a character followed by the name and and endline.
         out << (char)I_arr[i].id << I_arr[i].name << endl;
      }

      // close the file and exit function
      out.close();
      return;
   }

   // If we got this far, the file didn't open.
   cout << "Error opening file: " << I_file << endl;
}


I think it may be some automatic translations being done when saving as a text file. I'm not even sure how to go about formatting the file in binary. Anyone able to help?
Here is the whole class for refference:

I_List.h
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
#include <iostream>
#include <string>
#include <fstream>

using namespace std;

const int initarr = 10; // Initial array size
const int chrsize = 50; // char array size for cin.getline

struct Item
{
   int id; // ID that uniqly defines the item
   string name; // Name of item
};

class I_List
{
   public:
      I_List () {} // Default constructor

      I_List (string filename); // My constuctor
      ~I_List (); // Destructor

      int I_additem (string IName); // Add items to array
      void I_edititem (int index, string IName); // Replaces Name of item at index

      int I_count (); // returns number of elements
      int I_capacity (); // returns array capacity

      Item I_getitembyindex (int index); // returns item at index
      int I_getindexbyid (int id); // returns index by id

   private:
      Item * I_arr; // The array
      string I_file; // The file name to save to.

      int I_top; // index of last entry added to array.
      int I_cap; // capacity of array.

      void I_grow (); // Double array size.
      void I_sort (); // Sort the array.

      void I_getfile (); // populate array from file
      void I_putfile (); // Save array to file
};


and I_List.cpp
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#include <iostream>
#include <string>
#include <fstream>
#include "I_List.h"

using namespace std;

I_List::I_List (string filename)
    : I_file (filename), I_cap (initarr), I_top (0)
{
   I_arr = new Item [initarr]; // Initialize array
      I_getfile(); // Load from the file if exists.
}

I_List::~I_List ()
{
   if (I_top > 0) // If there is any data in the array
      I_putfile(); // write the data to the file

   delete []I_arr; // Delete array
}

int I_List::I_additem (string IName)
{
   // First, see if array is full.
   if (I_top >= I_cap)
      I_grow(); // Double size of array.

   // Now we add to the array
   I_arr[I_top].name = IName; // set the name
   I_arr[I_top].id = I_top; // the index is the id (it gets sorted)

   I_top++; // Increase I_top (needs to be done before sort)

   if (I_top > 1) // Make sure there are enough to warrent a sort
      I_sort(); // See?! I told you it gets sorted

   return I_top - 1; // return the index (the one before we increased I_top)
}

void I_List::I_grow ()
{
   // allocate new array
   Item * I_new = new Item [2 * I_cap];

   // copy all the entries
   for (int i = 0; i < I_cap; ++i)
   {
   I_new[i] = I_arr[i];
   }

   I_cap *= 2; // update I_cap

   delete []I_arr; // Free old memory

   I_arr = I_new; // Substitude new array for old
}

void I_List::I_sort () // A bubble sort is not the most efficient, but should do fine here.
{
   // This should always be done before getting this far, but I check it anyway.
   if (I_top <= 1) // Check if there are 0 or 1 items. No need to sort.
   {
      return;
   }

   // Now I can start sorting.
   for (int i = (I_top - 1); i > 0; i--)
   {
      for (int j = (i - 1); j >= 0; j--)
      {
         // Note: Need to create another file with a tolower() function.
         // basically, if char >= 'A' && char <= 'Z' then char += 'a' - 'A'
         if (I_arr[i].name.compare(I_arr[j].name) < 0)
         {
            Item temp = I_arr[i];
            I_arr[i] = I_arr[j];
            I_arr[j] = temp;
         }
      }
   }
}

int I_List::I_count ()
{
   return I_top; // return first available index which is also the count
}

int I_List::I_capacity ()
{
   return I_cap; // return the current array capacity
}

Item I_List::I_getitembyindex (int index)
{
   return I_arr[index]; // return the item at index
}

int I_List::I_getindexbyid (int id)
{
   for (int i = 0; i < I_top; ++i)
   {
      if (I_arr[i].id == id) // search for item with desired id
         return i; // return index of found item
   }
   return -1; // if not found, return -1
}

void I_List::I_edititem(int index, string IName)
{
   I_arr[index].name = IName; // Change Name at index
   if (I_top > 1) // check if we have enough to sort by
      I_sort(); // Sort the array.
}

void I_List::I_getfile ()
{
   ifstream in; // open a file for reading
   in.open(I_file.c_str(), ios::in);

   if (in.is_open()) // If the file opened successfully
   {
      char line[chrsize]; // Get the first line
      in.getline(line,chrsize);

      I_top = 0; // It should already be zero, but just in case

      while (!in.eof()) // While not end of file
      {
         // Check array count and capacity and grow if needed
         if (I_top >= I_cap)
            I_grow();

         I_arr[I_top].id = (int)line[0]; // Set the ID

         char temp[chrsize]; // Char array to copy to
         int i = 0; // iterator

         while (line[i] != '\0') // while not terminating char
         {
            temp[i] = line[i+1]; // skips first char, and grabs terminator
            ++i;
         }

         I_arr[I_top].name = temp; // Assign the Name
         ++I_top; // go to next item

         in.getline(line,chrsize); // get the next line
      }

      in.close(); // close the file

      if (I_top > 1) // check if we have enough items
         I_sort(); // and sort the array
      return;
   }

   // If we got this far, file didn't open
   cout << "Error opening file: " << I_file << endl;
}

void I_List::I_putfile ()
{
   ofstream out; // open the file for writing; replace everything in it.
   out.open(I_file.c_str(), ios::out | ios::trunc);

   if (out.is_open()) // if file opened successfully
   {
      for (int i = 0; i < I_top; i++) // for each item in array
      {
         // write the id as a character followed by the name and and endline.
         out << (char)I_arr[i].id << I_arr[i].name << endl;
      }

      // close the file and exit function
      out.close();
      return;
   }

   // If we got this far, the file didn't open.
   cout << "Error opening file: " << I_file << endl;
}
Ok. I have a question.

1) is this for an assignment/homework?

My answer will vary based on how you answer this.

Edit: http://www.cplusplus.com/forum/general/2417/ is probably going to be a good reference for you. This shows how to do Binary IO.
Last edited on
no.. i've never taken any programming classes, though more and more I am starting to think I should. I know someone once told me to use STL set or map, but the more I played with them the less they seem to fit the bill, so I decided to just create a class from scratch.

Looking at that link now..

Everything I know about programming i've learned from online tutorials. I'd love to find a programmer who lived nearby who could work with me one on one on a lot of things.. but..
Last edited on
ok. Typical Assignment/Homework questions have constraints on what you are allowed to use.

If you are doing Binary IO (with Structs/Classes). You can actually write the object directly to file and reload it directly from file without having to manually copy the member variables around. See that link I provided up a bit.

If you are doing text IO. I highly recommend against using a character buffer. Try something like
1
2
string input;
getline(file_io_stream, input);


That will retrieve a line and put it into stream. You won't be constrained by the size of your buffer.

Edit: STL Vector is a container that you might find useful. It seems to fit your situation perfectly.
Last edited on
I tried playing aroudn with a set (has the sort function that i could specify) and was able to pass it a sort function as such:
1
2
3
4
5
6
class sortitm {
   bool operator() (Item a, Item b)
   {
      return (a.name < b.name);
   }
}


But I was unable to figgure out how to get it to do a find by Item.id
and while Vector (which has no sort parameter to pass a sort function to) allowed me to return an item by index, set did not.

I spent a week trying to use any STL container for this. I gave up, and wrote this class in 12 hours.

edit: Oh yea, and the reason i'm using character buffers, I was using (am still using) filestream.getline which won't take a string. I could only get it to work with a character buffer.

However, i notice above, that you're not using filestream.getline(buffer,count); but rather just getline(filestream,string);

I may try making that change and see how that effects it. Though then I'd have to figgure out how to seperate the integer ID from the string Name.
Last edited on
ok.. i can see what mistake i was making, in assuming (char)int would result in the same thing. where an int consists of 4 chars. ick.

I've read the link, and looked up the binary i/o on here, and googled the bit shift thing to fathom what i can on that, and have a decent understanding of what they are doing in that post. I should be able to do it, by writing 4 bytes for the int, followed by the string.c_str() (just to be certain there is no conflict by trying to write string normally)

but the difficult part then.. is figguring out how to read it back in..
getting the original int would be a simple matter.. but the dynamically sized string.. I can only think to loop forward, find a terminating null (zero), to get the byte size to read in.

Or, include a second integer with the value set to the number of bytes that the following string contains. (i think the first one would be better).

I'll see what I can do with it. Thanks again.
Ok.. I did this up as a seperate program to test out the code, and make sure it works before I add it to my class. Could anyone give it a quick going over and give any advice as to how i may clean it up a bit before I transfer the important parts over to my class?

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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <cstdlib>
#include <iostream>
#include <string>
#include <fstream>
#include <time.h>

using namespace std;

const bool write = false; // change this to false after writing to file

struct Item {
   int id;
   string name;
};

int main(int argc, char *argv[])
{

   Item * _arr = new Item [10];

   string array[7] = {"Ice", "Cold", "Bud", "Light", "Makes", "Me", "Drunk"};

   if (write == true)
   {
      cout << "Entered Write Segment." << endl;

      srand(time(NULL));

      for (int i = 0; i <= 6; ++i)
      {
         int x = (rand() % 100);

         _arr[i].id = x;
         _arr[i].name = array[i];
      }

      ofstream out; // open file
      out.open("myfile.dat", ios::out | ios::trunc | ios::binary);

      if (!out.is_open())
      {
         cout << "Could not open file for writing." << endl;
         system("PAUSE");
         return 0;
      }

      cout << "File open for writing." << endl;

      for (int i = 0; i <= 6; ++i)
      {
         int n = _arr[i].id; // get the id
         char bytes[4];

         for (int j = 3; j >= 0; j--, n >> 8)
            bytes[j] = n & 0xFF;

         out.write(bytes, 4); // write the bytes to the file

         out.write(_arr[i].name.c_str(), _arr[i].name.length() + 1); // write the name
      }

      out.close();

      cout << "File closed." << endl;

      for (int i = 0; i <= 6; ++i)
      {
         cout << _arr[i].id << ". " << _arr[i].name << endl;
      }
   }

   else
   {
      cout << "Entered Read Segment." << endl;

      ifstream in; // open file
      in.open("myfile.dat", ios::in | ios::binary);

      if (!in.is_open())
      {
         cout << "Could not open file for reading." << endl;
         system("PAUSE");
         return 0;
      }

      cout << "File is open for reading." << endl;

      int i = 0; // set iterator

      char bytes[4];
      in.read(bytes,4); // get first 4 bytes

      while (!in.eof())
      {
         int n = 0;
         for (int j = 0; j <= 3; j++, n << 8) // convert to integer
            n = n | (bytes[j] & 0xFF);

         _arr[i].id = n; // set id

         char *t = new char; // allocate a single char memory_block
         in.read(t,1); // get first letter of string

         while (*t != '\0')
         {
            _arr[i].name.push_back(*t);
            in.read(t,1); // get next letter of string.
         }

         delete t; // free up block
         i++; // increase iterator

         in.read(bytes,4); // get next 4 bytes
      }

      in.close();

      cout << "File closed." << endl;

      for (int i = 0; i <= 6; ++i)
      {
         cout << _arr[i].id << ". " << _arr[i].name << endl;
      }

   }

   delete [] _arr;

   system("PAUSE");
   return EXIT_SUCCESS;
}


Thanks
Topic archived. No new replies allowed.