using fstream objects properly, I can't see the problem

Hey, I have been trying to learn how to use files and fstreams as input/output for practice before I try writing something that I have had in mind for quite some time. I have done this before in another example problem and it seemed to work fine but following the same sort of template I cant get this to work. It will create the file "nameage.dat" which it is suppose to at the beginning and initialize the file with 100 records of "id lastname firstname age" where id is 1...100 and lastname = "unassigned" firstname = "" and age = 0.

That part goes fine, if you compile the program and run the first option it will print out just fine. If you then either update or delete a record from this file it seems to be ok but then when you go to list the file again it will print the banner (ie "ID Last Name First Name Age" and then just kind of stalls out like it is stuck in an infinite loop.

Maybe there is something obvious i am not seeing in my code but I also tried to set it up before this so that each function would open use then close the file itself but when i did that it would initialize fine but after changing something then trying to show the list again it would list everything up to the point you changed something and then exit with -1 0xFFFFFF.

Any ideas?

Thanks in advance.

edit1: you should also note that this is to print non-std type classes, so thats what the reinterpret_cast< char * >( &person) is about, the PersonData object shouldn't have anything to do with the problem I am having but I can include it if it would help, just tell me.

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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <iomanip>
#include "PersonData.h"
using namespace std;

void outputLine( ostream &, const PersonData &);
void updateRecord( fstream & );
void printBanner( ostream & );
void outputAllRecords( fstream & );
void deleteRecord( fstream & );
int enterChoice();

int main()
{
    fstream initialRecord( "nameage.dat", ios::out | ios::binary );
    // initialize file with 100 entries with default values
    for (int i = 1; i <=100; i++ )
    {
        if ( !initialRecord )
        {
            cerr << "Error opening file" << endl;
            exit( 1 );
        }

        PersonData blankPerson;
        blankPerson.setId( i );

        // seek correct file position to write to
        initialRecord.seekp( ( blankPerson.getId() - 1) * sizeof( PersonData ) );

        // write new data at new position
        initialRecord.write( reinterpret_cast< const char * >( &blankPerson ),
            sizeof( PersonData ) );
    }

    initialRecord.clear();
    initialRecord.close();

    fstream inOutRecord( "nameage.dat", ios::in | ios::out | ios::binary );

    enum Choices { PRINT=1, UPDATE, DELETE, END };

    int choice;

    while ( (choice = enterChoice())  != END )
    {
        switch ( choice )
        {
            case PRINT:
                outputAllRecords( inOutRecord );
                break;
            case UPDATE:
                updateRecord( inOutRecord );
                break;
            case DELETE:
                deleteRecord( inOutRecord );
                break;
            default:
                cerr << "incorrect choice" << endl;
                break;
        }
    }
    return 0;
}

int enterChoice()
{
    cout << "\nEnter your choice" << endl
        << "1 - Show all current records" << endl
        << "2 - Update a record" << endl
        << "3 - Delete a record" << endl
        << "4 - End Program\n?";

        int choice;
        cin >> choice;
        return choice;
}

 // output line function start
void outputLine( ostream &output, const PersonData &person )
{
    output << left << setw( 10 ) << person.getId()
        << setw( 16 ) << person.getLastName()
        << setw( 11 ) << person.getFirstName()
        << setw( 10 ) << setprecision( 2 ) << right << fixed
        << showpoint << person.getAge() << endl;
} // end function outputLine

// print banner before outputLine, should be called before outputLine
void printBanner( ostream &output )
{
    output << left << setw( 5 ) << "ID" << setw( 16 )
    << "Last Name" << setw( 11 ) << "First Name" << left
    << setw( 20 ) << right << "Age" << endl;
}

void updateRecord( fstream &fileToUpdate)
{
    int id;
    string lastName;
    string firstName;
    int age;

    cout << "Enter ID number (1 to 100):\n? ";

    // require user to specify an account
    PersonData person;
    cin >> id;

    while ( id > 0 && id <= 100)
    {
        // user enters last name, first name, and age
        cout << "Please enter lastName, firstName, and age:\n?";
        cin >> lastName;
        cin >> firstName;
        cin >> age;

        // record the new data into the person object
        person.setId( id);
        person.setLastName( lastName );
        person.setFirstName( firstName );
        person.setAge( age );

        // set file-position in user specified file
        fileToUpdate.seekp( ( person.getId() - 1) * sizeof( PersonData ) );

        // write user-specified information to file
        fileToUpdate.write( reinterpret_cast< const char * >( &person ),
            sizeof( PersonData ) );

        // enable user to enter another id number
        cout << "Enter ID Number:\n?";
        cin >> id;
    } // end while
} // end function updateRecord

void outputAllRecords( fstream &fileToPrint )
{
    printBanner( cout );

    // initalize PersonData object
    PersonData person;

    fileToPrint.seekg( 0 );

    // read first record
    fileToPrint.read( reinterpret_cast< char * >( &person ),
        sizeof( PersonData ) );

    // read in all records from file
    while ( !fileToPrint.eof() )
    {
        if (person.getId() != 0 )
        {
            outputLine( cout, person );
        }

        // read in next entry
        fileToPrint.read( reinterpret_cast< char * >( &person ),
            sizeof( PersonData ) );
    }
}

void deleteRecord( fstream &fileToDelete )
{
    cout << "Please enter the id you would like to delete: ";
    int id;
    cin >> id;
    cout << "This record will be deleted: " << endl;

    PersonData person;
    person.setId( id );

    fileToDelete.seekp( ( person.getId() - 1 ) * sizeof( PersonData ) );
    fileToDelete.read( reinterpret_cast< char *>( &person ),
        sizeof( PersonData ) );

    printBanner( cout );
    outputLine( cout, person );

    cout << "\nIs this what you want to do? (y/n): ";
    string answer;
    cin >> answer;
    if ( answer == "y" )
    {
        fileToDelete.seekp( ( person.getId() - 1) * sizeof( PersonData) );
       person.setLastName( "unassigned" );
       person.setFirstName( "" );
       person.setAge( 0 );
       fileToDelete.write( reinterpret_cast< const char * >( &person ),
        sizeof( PersonData) );

    }
    else
    {
        cout << "\nThe record was  not modified\n" << endl;
    }
}
Last edited on
> the PersonData object shouldn't have anything to do with the problem I am having
http://www.cplusplus.com/forum/general/105078/#msg567353

> and then just kind of stalls out like it is stuck in an infinite loop
Run through a debugger, when it stalls simply interrupt your program.
You would be able to see what was trying to do
Forgive me as I am new to using debuggers to find problems in m program, I got it set up, at least I think I did it right, I set a break point when it sets seekg(0) in showAllRecords() and followed it through, after altering the data in the file the program would get stuck at the "if (person.getId() != 0)" check and would just bounce through line 150 to 155, never getting to the print line or to read the next set of data.

So I removed the if check and now it just crashes with a segmentation fault, I dont know how to get more information about why this is but it references the PersonData object, with i assume a stack trace:

#0 0x7ffff69c63d0 ??() (/lib64/libc.so.6:??)
#1 0x7ffff7217532 std::string::_Rep::_M_clone(std::allocator<char> const&, unsigned long) () (/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/libstdc++.so.6:??)
#2 0x7ffff7217c3c std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&) () (/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/libstdc++.so.6:??)
#3 0x4029d9 PersonData::getLastName(this=this@entry=0x7fffffffdb40) (/home/bobby /cpp/FileIOPerson/PersonData.cpp:37)
#4 0x401bb3 outputLine(output=..., person=...) (/home/bobby/cpp/FileIOPerson/main.cpp:85)
#5 0x40234f outputAllRecords(fileToPrint=...) (/home/bobby/cpp/FileIOPerson/main.cpp:155)
#6 0x4016cd main() (/home/bobby/cpp/FileIOPerson/main.cpp:52)

It brings focus to the #3 line, PersonData:getLastName(this=this@entry0x7fffffffdb40) so Ill put the code for PersonData up to but if you could just tell me where this is asking me to look at then id be grateful, because there is nothing wrong with getLastName to me, its just a return statement.

PersonData.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
#ifndef PERSONDATA_H
#define PERSONDATA_H
#include <string>
using namespace std;

class PersonData
{
public:
    PersonData( int = 0, string = "unassigned", string = "", int = 0);

    // accessor functions for id
    void setId( int );
    int getId() const;

    // accessor functions for lastName
    void setLastName( string );
    string getLastName() const;

    // accessor functions for firstName
    void setFirstName( string );
    string getFirstName() const;

    // accessor functions for age
    void setAge( int );
    int getAge() const;

private:
    string lastName;
    string firstName;
    int age;
    int id;

};


#endif // PERSONDATA_H 


and PersonData.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
#include <string>
#include "PersonData.h"

using namespace std;

// default constructor
PersonData::PersonData( int idValue, string lastNameValue, string firstNameValue,
    int ageValue )
{
    setId( idValue );
    setLastName( lastNameValue );
    setFirstName( firstNameValue );
    setAge( ageValue );
} // end function PersonData

// set ID
void PersonData::setId( int idValue )
{
    id = idValue;
} // end function setId

// get ID
int PersonData::getId() const
{
    return id;
} // end function getId

// set last name
void PersonData::setLastName( string lastNameValue )
{
    lastName = lastNameValue;
} // end function setLastName

// get last name
string PersonData::getLastName() const
{
    return lastName;
} // end function getLastName

// set first name
void PersonData::setFirstName( string firstNameValue )
{
    firstName = firstNameValue;
} // end function setFirstName

// get first name
string PersonData::getFirstName() const
{
    return firstName;
} // end function getFirstName

// set age
void PersonData::setAge( int ageValue )
{
    age = ageValue;
} // end function setAge

// get age
int PersonData::getAge() const
{
    return age;
} // end function getAge 


Thanks....
Read the link, you shouldn't mess with the internals of your objects.
The debugger is pointing at string's copy constructor, which is failing to make a copy of the trashed person.lastname, and that is trashed because you ran fileToDelete.read( reinterpret_cast< char *>( &person ), sizeof( PersonData ) );

I would recommend providing operator>> and operator<< for PersonData which would read/write its contents using normal, text I/O.. or follow the other options on that stackoverflow link in your other thread.
Last edited on
Thanks for the advice, I'll try to get it implemented the way I had it written originally but I went back and looked closer at the program I wrote before that did basically this same thing but worked and what I did wrong (or different i guess is a better word, im sure there is a way to get it to work the way I show here) is that the private members of PersonData lastName and firstName should be char arrays not strings (which is really the same thing, I know, but char arrays are C type strings and "strings" are C++ type string, right?) but after changing them to char arrays and updating the member functions to end the array correctly ( append '\0' to the end) everything works as I would think it should.

Which it now makes more sense to me as to why you do a reinterpret_cast< char *>, since the member variables are char arrays. I still dont understand why I would want to overload the stream insert/out operators for PersonData when I supply member functions to output all their values any way but I'll try to figure it out with the stuff you have lniked me to.

Thanks for the help!
Topic archived. No new replies allowed.