-5

So basically I'm supposed to make a database like application in c++. The problem I have is when I try to save/read from the .txt files I've created. Classes definitions and output functions:

file:table.h

#ifndef TABLE_H__
#define TABLE_H__

#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include<functional>
#include<cstdio>

using namespace std;

class tableColumn
{
public:
    string colName;
    vector<string> tableCol;

    tableColumn() {};
    ~tableColumn() {};

    string getColName() { return colName; }
    void setColName(string);
    int getColSize() { return tableCol.size(); }

};


void tableColumn::setColName(string a)
{
    colName = a;
}

class tableMatrix
{
private:
    vector<tableColumn> table;
    string tableName;
public:
    tableMatrix() {};
    ~tableMatrix() {};

    string getTableName() { return tableName; }
    void setTableName(string);

    void saveToFile(string);
    void readFromFile(string);
};


void tableMatrix::saveToFile(string tableFileName)
{
    string aux;
    aux += tableFileName;
    aux += ".txt";

    ofstream out(aux.c_str());

    out << tableName << endl;
    out << table.at(1).getColSize() << endl;
    out << table.size() << endl;

    for (int i = 0; i < (int)table.size(); i++)
    {
        out << table.at(i).colName<<endl;
        for (int j = 0; j < (int)table.at(i).tableCol.size(); j++)
        {
            out << table.at(i).tableCol.at(j) << endl;
        }
    }
    out.close();
}

void tableMatrix::readFromFile(string tableFileName)
{
    string aux,element,space;
    int rows, cols;
    aux += tableFileName;
    aux += ".txt";

    ifstream in(aux.c_str());
    tableColumn auxc;

    getline(in, tableName);
    in >> rows;
    in >> cols;

    for (int i = 0; i < cols; i++)
    {
        in >> auxc.colName;
        for (int j = 0; j < rows; j++)
        {
            in >> element;
            auxc.tableCol.push_back(element);
        }
        table.push_back(auxc);
        auxc.~tableColumn();
    }
    in.close();
}

#endif

file:database.h

#ifndef DATABASE_H__
#define DATABASE_H__

#include<iostream>
#include<fstream>
#include<vector>
#include<algorithm>
#include<string>
#include"table.h"
#include<cstdio>

using namespace std;

class database
{
private:
    vector<tableMatrix> db;
public:
    database() {};
    ~database() {};

    void saveToFile();
    void readFromFile();
};


void database::saveToFile()
{

    ofstream out("tablenames.txt");

    for (int i = 0; i < (int)db.size(); i++)
    {
        out << db.at(i).getTableName() << endl;
    }
    out.close();

    vector<string> filenames;
    string aux;

    for (int i = 0; i < (int)db.size(); i++)
    {
        aux = db.at(i).getTableName();
        filenames.push_back(aux);
    }
    for (int i = 0; i < (int)db.size(); i++)
    {
        db.at(i).saveToFile(filenames.at(i));
    }
}

void database::readFromFile()
{
    vector<string>filenames;
    string aux;
    ifstream in("tablenames.txt");
    tableMatrix auxt;

    while (!in.eof())
    {
        in >> aux;
        filenames.push_back(aux);
    }
    for (int i = 0; i < (int)filenames.size(); i++)
    {
        auxt.readFromFile(filenames.at(i));
        db.push_back(auxt);
    }
    in.close();
}

#endif

main.cpp:

#include "stdafx.h"
#include<iostream>
#include<fstream>
#include"database.h"
#include<sstream>
#include<Windows.h>
#include<conio.h>
#include<vector>

using namespace std;

int main()
{
    database A;
    A.readFromFile();
    A.saveToFile();

    return 0;
}

file : tablenames.txt

aaa
asa2

file : aaa.txt

aaa
3
2
col1
1
2
3
col2
4
5
6

file : asa2.txt

asa2
3
2
col1
1
2
3
col2
4
5
67

Basically I want to create a txt file with all table names (tablenames.txt) and then a txt file for each table with its name and column data. However when I try to run it, it's duplicating my last entry in tablenames.txt and it messes up my table data by adding additional columns and/or rows.

For example, tablenames.txt : ramdomname1, ramdomname2 becomes: ramdomname1,ramdomname2,ramdomname2. Same deal with the other table files except that it does it on a larger scale (2 rows become 10 for example with duplicated/scrambled data).

bf16
  • 37
  • 1
  • 9
  • 1
    And your question actually is? – πάντα ῥεῖ May 29 '14 at 13:18
  • 1
    Please post an [SSCCE](http://sscce.org/) and your actual question. – Baum mit Augen May 29 '14 at 13:19
  • the question : what is the problem and how to "fix" it so that it does what I want it to do. It may be something obvious but I can't see it. – bf16 May 29 '14 at 13:21
  • I'd recommend debugging your program, step through line by line and see what actually happens. Also possibly related [`while (!in.eof())`](http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong) – πάντα ῥεῖ May 29 '14 at 13:26
  • already done that and I can't see any problems. – bf16 May 29 '14 at 13:30
  • @bf16: I get a whole screen full of compiler errors with your code, even after adding the necessary headers, `using namespace std` and rearranging the classes with dependencies between them. I tried isolating your problem with a smaller piece of code, but that worked fine. You need to post compilable code, or else we are left with guessing. – Christian Hackl May 29 '14 at 13:32
  • @bf16 it really means you need to try to compile that code, not just edit it and assume it's all right. Until you get something testable by people here, you likely won't get any answer. *Please* check your code with a compiler. And please don't get offended, it's usual practice here to downvote for that reason. It's nothing personal, you just need to fix your question. – didierc May 29 '14 at 13:35
  • Also: `auxc.~tableColumn();` ... That doesn't feel right at all. – didierc May 29 '14 at 13:36
  • You still need to provide `tableColumn::getColSize`. – didierc May 29 '14 at 13:44
  • 1
    I'm really sorry, it's just that I'm in a rush and the project is much bigger that what the problem is. Edited my post with full details. It should compile now. – bf16 May 29 '14 at 13:47

1 Answers1

1

Your output is correct, but your input is wrong. You are not supposed to use eof like this. The idiomatic loop goes like this:

while (in >> aux)
{
    filenames.push_back(aux);
}

Look at thousands of past Stack Overflow questions and answers about using C++ streams in loops.

Let's have a look again at your original code:

while (!in.eof())
{
    in >> aux;
    filenames.push_back(aux);
}

What happens here is that when EOF is encountered, no error has occurred yetthe EOF flag is only set when a read operation actually encountered EOF, so the loop condition still holds. The following call to operator>> does set the stream to an error stateEOF, but it's too late. The operation leaves aux unmodified, i.e. with the same value as before. That unmodified value is then pushed back to the vector again. Afterwards, the loop condition is checked again and finally fails (because the stream is in an error state).

In fact, for an even more robust approach, use the free-standing std::getline function and check every table name for correctness (e.g. not allowed characters, such as spaces or tabs):

std::string line;
while (std::getline(in, line))
{
    if (isCorrect(line))
    {
        filenames.push_back(line);
    }
    else
    {
        // error handling
    }
}

Edit: Some corrections on the explanation of original code.

Christian Hackl
  • 25,191
  • 3
  • 23
  • 53
  • and the `auxc.~tableColumn();` which makes the program segfault on my system, and the method definitions in headers, and the `using namespace` in headers, and perhaps the windowism at the beginning of `main.cpp` (that's ... ok maybe). – didierc May 29 '14 at 14:22
  • @didierc: In fact, the code could be improved a lot. I also dislike the C-style casts and the fact that no iterators are used for vector iteration. – Christian Hackl May 29 '14 at 14:25
  • yeah, I suppose so. Here's another way of writing the loop : `while (in >> aux && in.good()) { /* input check */ } // io error handling`. – didierc May 29 '14 at 14:28
  • I see, thanks a lot the explanations,very helpful! I only have one more question since you mentioned it : what is wrong with using the casts ? If I remove those I get warnings ( that's the reason they're there in the first place). – bf16 May 29 '14 at 14:44
  • @bf16: C-style casts only exist for C compatibility. In C++, you use one of several specific casts. In this case, `static_cast(db.size())` will do fine. However, the idiomatic way to iterate over a container is with iterators, where the problem disappears entirely. Look here for a quick overview: http://en.wikipedia.org/wiki/Iterator#C.2B.2B – Christian Hackl May 29 '14 at 14:51