Sposob na zapisywanie plikow z roznymi indeksami

0

Witam. Napotkałem kolejny problem odnośnie zapisywania wielu elementow do plikow z zmienionymi indeksami np Osoba0.txt, Osoba1.txt itd.

Opis Programu
Program powinien dodawać użytkowników, a po ponownym uruchomieniu wczytać ich z pliku i móc dodać nowych.
1 Uruchomienie zapisuje "Jan", "Dawid", "Mateusz"
2 Uruchomienie zapisuje "Jan", "Dawid", "Mateusz", "Jan", "Dawid", "Mateusz".
itd...
Istotne jest aby kolejne dodania nie nadpisywały poprzednich. Tak niestety się dzieje teraz w programie.
Próbowałem na wiele sposobów. dodawać id++ w Konstruktorach operatorach, przypisania, odejmować w destruktorze. Zawsze coś było nie tak.

Pytania:

  1. Do Klasy Osoba zaimplementowałem funkcje ZapiszDoPliku() i WczytajZPliku(). Czy to mądre posunięcie?
  2. W jaki sposób poradzić sobie z głównym problemem wątku ?
    Siedzę nad tym od 10 godziny :)...

Program

#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

// Konwersja
string intToStr(int n)
{
    string tmp;
    if(n < 0)
    {
        tmp = "-";
        n = -n;
    }
    if(n > 9)
        tmp += intToStr(n / 10);
    tmp += n % 10 + 48;
    return tmp;
}
//********************************************************************************************************************
class Osoba
{
public:
    static int id;
    string imie; // dla  uproszczenia publicznie


    Osoba(string imie): imie(imie)
    {
        id++;
        cout << "Konstruktor param.     id:" << id << endl;
    }

    Osoba()
    {
        // cout << "Konstruktor domyslny   id:" << id << endl;
        //id++;
    }

    ~Osoba()
    {
        // cout << "Destruktor             id:" << id << endl;
        // id--;
    }

    void ZapiszDoPliku()
    {
        fstream plik;
        string nazwa = "Osoba" + intToStr(id) + ".txt";
        plik.open(nazwa.c_str(), ios::out);
        plik << imie;
        plik.close();
    }

    bool WczytajZPliku(int id)
    {
        fstream plik;
        string linia;
        string nazwa = "Osoba" + intToStr(id) + ".txt";

        plik.open(nazwa.c_str(), ios::in);


        if(plik.good())
        {
            cout<< "Wczytuje: " << nazwa << endl;
            while(getline(plik,linia))
                imie = linia; // jest tylko jeden przypadek dla tego programu,
            return true;
            plik.close();
        }
        else
            cout <<"Wczytano Osob: " << id << endl;
        plik.close();
        return false;
    }
};
int Osoba::id =-1; //  konstruktor zwieksza++, wiec pliki beda zapisane od 0
//********************************************************************************************************************
void WczytajListeOsobZPliku(vector<Osoba> &listaOsob)
{
    Osoba o;
    for(int i=0; ; i++)              // nie wiem ile jest plikow osob wiec wykonuje az znajdzie ostatatni z id
        if(o.WczytajZPliku(i))       // Gdy uda sie wczytac
            listaOsob.push_back(o);  // dodaj na liste osobe uzupelniona imieniem
        else
            break;                   // jezeli nie znaleziono pliku o nazwie z tym indeksem opusc petke
}
//********************************************************************************************************************
void WypiszListeOsob(vector<Osoba> &listaOsob)
{
    for(int i=0; i<listaOsob.size(); i++)
        cout << listaOsob[i].imie << endl;
}
//********************************************************************************************************************
void DodajOsobeDoListy(vector<Osoba> &listaOsob)
{
    string imiona[] = {"Jan", "Dawid", "Mateusz"};
    for(int i=0; i<3; i++) // dodaj  osoby
    {
        Osoba o(imiona[i]);
        listaOsob.push_back(o);
        listaOsob[i].ZapiszDoPliku();
        cout << "Osoba Dodana!" << endl;
    }
}
//********************************************************************************************************************
int main()
{
    vector<Osoba> listaOsob;
    WczytajListeOsobZPliku(listaOsob);   //Przed rozpoczeciem wczytujemy z pliku

    WypiszListeOsob  (listaOsob);        // wypisz osoby  wczytane z pliku
    DodajOsobeDoListy(listaOsob);
    WypiszListeOsob  (listaOsob);        // wypisz osoby wczytane z pliku i nowe dodane

    return 0;
}
 
1

czemu chcesz miec
Osoba0.txt
Osoba1.txt

Po co Ci w Osoba ID?
Brakuje CI flagi App do dodawania kolejnych rekordow. Out bedziesz otwierac plik na nowo i go nadpisywal
Czemu co kazdy obiekt chcesz tworzyc nowy plik? To jest bez sensu... To powinno byc w jednym pliku...
jezeli plik sie nie otworzyl (wczytajZPliku) to nie moze wczytac zadnej osoby wiec ten else jest bez sensu
Oczywiscie nie zamykasz pliku odpowiednio po POPRAWNYM wczytaniu.
nie uzywaj golych tablic, od tego jest vector. Vector mozesz tak samo zainicializowac jak tablice...
nie uzywaj magic numberow...

Ale jezeli tak chcesz zrobic by tworzyl odpowiednio kolejne elementy. To co musisz zrobic jest nastepujace

  1. Utworzyc folder data (albo jakis inny pusty folder)
  2. tworzyc pliki tam
  3. zliczasz ile jest plikow w tym folderze http://stackoverflow.com/questions/612097/how-can-i-get-the-list-of-files-in-a-directory-using-c-or-c
  4. tworzysz kolejny indeks na oparciu z punktu 3) +1

Osobiscie sam nie tworzylbym kilku plikow bo to nie ma najmniejszego sensu. Po prostu robilbym rekordy i dodalbym do osoby pole ID (nie do klasy a w pliku), wiec byloby

  1. Mateusz
  2. Jan
  3. Piotr

I dzieki temu czytam sobie plik i wiem ile jest osob i jak dodac nastepna (mozna to zrobic na kilka sposobow, najprostszy bedzie bo i tak wczytujesz z pliku do pamieci to wiesz ile masz rekordow to po prostu zwiekszyc rekord o jeden. A plik otworzyc z flaga std::app

intToStr juz istnieje nazywa sie to_string
http://en.cppreference.com/w/cpp/string/basic_string/to_string

1

Co do kwestii trzymania danych osób w osobnych plikach, jak wyżej @fasadin napisał, to bez dwóch zdań niepotrzebnie komplikujesz i jest to bardzo złym pomysłem, przy większych modyfikacjach stracisz kontrolę, porzuć ten pomysł. Tworzysz jeden plik, w którym Twoje dane osoby będą zapisane linia po linii. I teraz jest kilka sposobów na manipulację tymi danymi. Możesz tak, najłatwiej: wczytujesz w programie tylko raz do vectora, czyli pamięci. Wszelkie operacje wykonujesz na nim, a po zakończeniu pracy z programem zapisujesz z powrotem do pliku (ma to oczywiście swoje słabe i dobre strony).

Zamiast funkcji ZapiszDoPliku, WczytajZPliku przeładuj operator <<, >> dla strumieni std::ostream, std::istream umożliwi Ci to przykładowo takie wczytywanie, wypisywanie:

std::copy(std::istream_iterator<Osoba>(plik), std::istream_iterator<Osoba>(), std::back_inserter(listaOsob));

std::copy(listaOsob.begin(), listaOsob.end(), std::ostream_iterator<Osoba>(cout, " "));
0

Jak zatem wczytywać dane, tak aby po napotkaniu pustej linii zakończyło się wczytywanie (wykorzystujac przeladowanie operatora>>) ?

 
// funkcja realizowana w Klasie Dziekanat zarzadza listaStudentow
    void WczytajZPliku()
    {
        fstream plik;

        plik.open("Student.txt", ios::in);
        Student wczytany;
        string linia;
        for(;;)
        {
            if(plik.good())
            {
                plik >> wczytany;
                listaStudentow.push_back(wczytany);
            }
            else
            {
                cerr << "Zakonczono wczytywanie" << endl;
                break;
            }
        }
        plik.close();
    }
 
ostream & operator<<(ostream & plik, Student & s) // zapisywanie do pliku
{
    return plik  << s.imie <<  "\n" << s.nazwisko << "\n" << s.login << "\n" << s.haslo << "\n" <<  s.kierunek << "\n"  << s.wydzial << "\n";
}

istream & operator>>(istream & plik, Student & s)
{
    return plik >> s.imie >> s.nazwisko >> s.login >> s.haslo >> s.kierunek >> s.wydzial;
}

Gdy zostawię na końcu pliku pustą linię. to Dubluje mi wczytany obiekt.
A z kolei jak usunę tą pustą linię. To przy zapisywaniu więcej obiektów ostatnia linia będzie sie nakładać z pierwsza bo nie ma znaku nowej linii

1
void WczytajZPliku()
{
	ifstream plik("Student.txt");

	if(plik.is_open())
	{
		copy(istream_iterator<Student>(plik), istream_iterator<Student>(), back_inserter(listaStudentow));

		plik.close();
	}
}
0

Dziękuję, poczytam zaraz o tej funkcji, aby zrozumieć jej działanie. W między czasie naszła mnie myśl, co w przypadku gdy np w Klasie Student mam zmienna typu vector<string> kursy;
zawartość może mieć różny rozmiar. Z Zapisaniem nie ma ma problemu wystarczy pętla.
Jak natomiast wczytywać takie dane?

  1. Czy użyć jakiegoś separatora przy na koncu zapisu do pliku, w przypadku jak w linii znajduje się jakiś znak to przerwać wczytanie.
  2. Po wczytaniu innych zmiennych(imie itd) podać ilość kursów na jakie zapisał sie student. I wiemy ile linni musimy jeszcze wczytać.
    Ktory z tych pomyslów jest lepszy? Chyba ze jest jakies lepsze rozwiazanie.
2
darthachill napisał(a):

Dziękuję, poczytam zaraz o tej funkcji, aby zrozumieć jej działanie.

Jeżeli na początek jest ona dla Ciebie mało zrozumiała to możesz tę linijkę zastąpić na np. taką:

for(Student student; plik >> student; listaStudentow.push_back(student));
darthachill napisał(a):

co w przypadku gdy np w Klasie Student mam zmienna typu vector<string> kursy;
zawartość może mieć różny rozmiar. Jak natomiast wczytywać takie dane?

Możesz to załatwić tak, że dane będziesz zapisywał i przechowywał w takiej postaci:

imie1 nazwisko1 login1 haslo1 kierunek1 wydzial1 kurs1 kurs2
imie2 nazwisko2 login2 haslo2 kierunek2 wydzial2 kurs1 kurs2 kurs3 kurs4
imie3 nazwisko3 login3 haslo3 kierunek3 wydzial3 kurs1
imie4 nazwisko4 login4 haslo4 kierunek4 wydzial4
imie5 nazwisko5 login5 haslo5 kierunek5 wydzial5 kurs1 kurs2 kurs3

Funkcja WczytajZPliku() nie ulegnie zmianie, natomiast Twoja funkcja istream& operator >>(istream& plik, Student& s) już tak. Musisz ją nieco zmodyfikować, np. tak:

friend istream& operator >>(istream& strumien, Student& s)
{
	string linia;

	if(getline(strumien, linia))
	{
		istringstream stringStream(linia);

		if(stringStream >> s.imie >> s.nazwisko >> s.login >> s.haslo >> s.kierunek >> s.wydzial)
		{
			s.listaKursow.clear();

			copy(istream_iterator<string>(stringStream), istream_iterator<string>(), back_inserter(s.listaKursow));
		}
	}

	return strumien;
}

czyli to działa tak, wczytujemy:

  1. kolejną linię z pliku,
  2. kolejno imię, nazwisko, login, hasło, kierunek, wydział (zakładam, że są one poprawne),
  3. kursy (jeśli jakiekolwiek są wpisane).

1 użytkowników online, w tym zalogowanych: 0, gości: 1