I'm developing a small database application that can add records to a file, but also modify them in a few ways. It keeps the records in a temporary object of my class PersonInfo.
The problem now is that it doesn't want to display things correctly, it seems to only be able to save the last record, then it replaces the previous records with weird characters.

OK, this is my code:

The PersonInfo class header file


#ifndef _PERSONINFO_H_
#define _PERSONINFO_H_

class PersonInfo {
private:
string name, city, country;
int age;
public:
PersonInfo();
~PersonInfo();
void DisplayDatabase(int ct);
void DisplayChangedDatabase(int ct, int whoHasStar);
void ChangePersonData(string Name, int Age, string City, string Country);

};

#endif

The PersonInfo class declaration file


#include <iostream>

using namespace std;

#include <iomanip>
#include <string>
#include "personInfo.h"


PersonInfo::PersonInfo() {
}

PersonInfo::~PersonInfo() {
}

void PersonInfo:: DisplayDatabase(int ct) {
int checkCtValue;

if (ct > 8)
checkCtValue = 19;
else
checkCtValue = 20;

cout << (ct + 1) << ": " << setw(checkCtValue) << name
<< setw(6) << age << setw(18) << city
<< setw(18) << country << "\n";
}

void PersonInfo:: DisplayChangedDatabase(int ct, int whoHasStar) {
int checkCtValue, starOff;

if (ct > 8)
checkCtValue = 19;
else
checkCtValue = 20;

cout << (ct + 1) << ": " << setw(checkCtValue) << name;

if ((ct + 1) == whoHasStar) {
cout << "*";
starOff = 5;
}
else {
cout << "";
starOff = 6;
}

cout << setw(starOff) << age << setw(18)
<< city << setw(18) << country << "\n";
}

void PersonInfo::ChangePersonData(string Name, int Age, string City, string Country) {
name = Name;
age = Age;
city = City;
country = Country;
}


If you wonder what whoHasStar is, it's a star printed after the name of the person, if that record has been edited.

So far so good, but the problem lies in my addRecord() function, the ability to add records to the end of the file.
Here's this code:

My addRecord() function


#include <iostream>
using namespace std;


#include <windows.h>
#include <string>
#include <fstream>
#include <iomanip>
#include <cstdlib>
#include "personInfo.h"


inline void eatline() { // I've never really understood what this does,
// and where in the code I need it, someone who knows?
while (cin.get() != '\n')
continue;
}


PersonInfo pInfo; // Temporary record keeper, the thing between reading and writing
const char *pFile = "personDB.dat"; // Main database file

fstream pfInOut; // File I/O-stream

int whoHasStar = 0; // Record number that has the star


////////////
// code
////////////


void addRecord() { // Adds one or more posts to the end of file

string Name, City, Country;
int Age;
char _Name[20], _City[20], _Country[20]; // This is because cin.get() won't work with strings! Any other workaround?


int ct = 0; // Record counter


pfInOut.open(pFile, ios_base::in | ios_base::ate | ios_base:: out | ios_base::app | ios_base::binary); // Is this the correct way of opening the file? Is ios_base::ate necessary?


if (_open()) {

pfInOut.seekg(0);

cout << "\nThis is the current contents of the file \"" << pFile << "\":\n\n";
cout << "Rec #" << setw(18) << "Name" << setw(6) << "Age"
<< setw(18) << "City" << setw(18) << "Country" << "\n\n";

while (pfInOut.read((char *) &pInfo, sizeof pInfo)) { // Loops through the file, reads a record, and displays it, then continues to the next one
pInfo.DisplayDatabase(ct); // Should I use sizeof pInfo or sizeof PersonInfo?
ct++;
}
if (pfInOut.eof())
pfInOut.clear();
else {
cerr << "Error while reading file \"" << pFile << "\".\n";
exit(1);
}
}

else {
cerr << "Couldn't open \"" << pFile << "\".\n";
exit(2);
}

if (pfInOut.eof())
pfInOut.clear();


cout << "\nEnter the name of the person: ";
eatline(); // Is this where I should put eatline()?
cin.get(_Name, 20);
//eatline(); // ... or should I put it here...
//while (_Name[0] != '\0') { // This is a problematic area, my app seems to be skipping this loop, so I've removed this while loop, why is it skipping it?
//eatline(); // ... or here?
cout << "Enter the age of the person: ";
cin >> Age;
eatline(); // Needed?
cout << "Enter the name of the new city: ";
cin.get(_City, 20);
eatline(); // Needed?
cout << "Enter the country: ";
cin.get(_Country, 20);
eatline(); // Needed?


Name = _Name; // Okidoki, defining char[] info to a string...
City = _City; // And here...
Country = _Country; // ... and here


pInfo.ChangePersonData(_Name, Age, _City, _Country);


pfInOut.write((char *) &pInfo, sizeof pInfo) << flush; // Writing the record to the end of file
if (pfInOut.fail()) {
cerr << "Error while attempt to write - bye.\n";
exit(5);
}

whoHasStar = ct + 1;

pfInOut.close();


viewChangedDatabase();

//}
}

It will display the records incorrectly, only showing the last one in a proper way.
Here is a screen of how it looks like:

[img][/img]


So there u have it, anyone who might know the solutions to the various problems here?

Help really appreciated, it means a lot to me to be able to finish this application.

Thanx in advance, guys!

/silkfire

Dani AI

Generated

— the symptom you describe (only the last record looks sane; previous entries turn to garbage) is exactly what happens when you raw-read/write an object that contains std::string. std::string holds pointers/internal state; read((char*)&obj, sizeof obj) copies that internal state to disk and back, producing invalid pointers and undefined behavior. is right: raw binary I/O only works for true POD types.

Two practical fixes (pick one):

  • Use a plain-text format (CSV, tab-separated, or one field per line). Read with std::getline() and parse. It’s simple, robust, and human-readable.
  • Use explicit binary serialization: write each string with a length prefix, write age as a fixed-size integer, and provide save/load methods on PersonInfo. That keeps binary I/O safe and portable within the same architecture.

Example of safe binary save/load for PersonInfo (conceptual):

// inside PersonInfo
void save(std::ostream& os) const {
    uint32_t n = (uint32_t)name.size();
    os.write((char*)&n, sizeof n);
    os.write(name.data(), n);
    os.write((char*)&age, sizeof age);
    // repeat for city, country
}
void load(std::istream& is) {
    uint32_t n;
    is.read((char*)&n, sizeof n);
    std::string tmp; tmp.resize(n);
    is.read(&tmp[0], n); name.swap(tmp);
    is.read((char*)&age, sizeof age);
    // repeat for city, country
}

I/O tips: read/display with std::ifstream fin(pFile, ios::binary), close it, then append with std::ofstream fout(pFile, ios::app|ios::binary). Don’t open with ios::app if you want to seek and overwrite records (ios::app forces writes to the end). If you need in-place edits, read all records into a std::vector<PersonInfo>, modify, then rewrite the file (open with ios::trunc).

Input handling: prefer std::string + std::getline() to avoid mixing operator>> and getline. If you must mix them, drop the leftover newline with std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n') (include <limits>). Only use fixed-size char[] if you intentionally design a POD struct for raw I/O—then carefully manage null-termination and truncation.

Recommended Answers

All 2 Replies

Your biggest problem is trying to use stream.read() and stream.write() with a non-POD type. Because the string class is not a trivial class, and your PersonInfo struct has at least one of them, read and write are only guaranteed to cause undefined behavior. You would be better off defining a function that extracts and inserts fields from/to the database manually. Using unformatted binary I/O is tricky to get right and has a lot of restrictions in C++.

Okay... so how should I do it with chars? I have problems with how I should create my function that takes char arrars (char[X]) as parameters...

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.