• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Usuwanie elementu z wektora

Object Storage Arubacloud
0 głosów
674 wizyt
pytanie zadane 18 sierpnia 2021 w C i C++ przez magda_19 Gaduła (3,080 p.)

Witam, piszę program 'książka adresowa'. Chcę dodać opcję usuwania osoby poprzez podanie liczby porządkowej. Po wpisaniu ID użytkownika, którego chcę usunąć np. ID = 2, program usuwa użytkownika z ID 1 i ID 2. Wiem, że błąd tkwi w funkcji erase, ale nie mam pomysłu jak to zapisać poprawnie.

#include <iostream>
#include <fstream>
#include <windows.h>
#include <cstdlib>
#include <sstream>
#include <vector>
#include <string>

using namespace std;

struct Adresat
{
    string imie, nazwisko, nrTel, email, adres;
    int idAdresata = 0;
};

std::vector <Adresat> adresaci;

int dodajOsobeDoKsiazki (int iloscOsob = 0)
{

    Adresat pomoc;

    string imie = " ", nazwisko = " ", nrTel = " ", email = " ", adres = " ";
    int idAdresata = iloscOsob + 1;

    std::string s = std::to_string(idAdresata);

    cout << "Podaj imie: ";
    cin >> imie;
    cin.sync();
    cout << "Podaj nazwisko: ";
    cin >> nazwisko;
    cin.sync();
    cout << "Podaj nr telefonu: ";
    cin >> nrTel;
    cin.sync();
    cout << "Podaj email: ";
    cin >> email;
    cin.sync();
    cout << "Podaj swoj adres: ";
    getline(cin, adres);

    pomoc.idAdresata = adresaci.size() + 1;
    pomoc.imie = imie;
    pomoc.nazwisko = nazwisko;
    pomoc.nrTel = nrTel;
    pomoc.email = email;
    pomoc.adres = adres;

    //dodaj osobe do wektora
    adresaci.push_back(pomoc);


    //zapisz do pliku
    fstream plikDoZapisu;
    plikDoZapisu.open("ksiazkaZAdresami.txt", ios::out | ios::app);

    if (plikDoZapisu.good() == true)
        {
            plikDoZapisu << pomoc.idAdresata << "|" << pomoc.imie << "|" << pomoc.nazwisko << "|" << pomoc.nrTel << "|" << pomoc.email << "|" << pomoc.adres << "|" << endl;
            plikDoZapisu.close();
        }
    else
        {
            cout << "Nie udalo sie otworzyc pliku";
            system("pause");
        }
    cout << "Osoba zostala dodana do ksiazki adresowej" << endl;
    system("pause");

    iloscOsob++;

    return iloscOsob + 1;
}

vector <Adresat> rozdzielDaneAdresatowNaPojedynczeDane (vector <Adresat> &adresaci, string liniaDoOdczytania, char znakPionowejKreski)
{
    Adresat adresat;
    int nrLinii = 1;
    stringstream ss(liniaDoOdczytania);
    string pojedynczaDana = " ";

    while (getline(ss, pojedynczaDana, znakPionowejKreski))
        {
            switch (nrLinii)
                {
                case 1:
                    adresat.idAdresata = (atoi(pojedynczaDana.c_str()));
                    break;
                case 2:
                    adresat.imie = pojedynczaDana;
                    break;
                case 3:
                    adresat.nazwisko = pojedynczaDana;
                    break;
                case 4:
                    adresat.nrTel = pojedynczaDana;
                    break;
                case 5:
                    adresat.email = pojedynczaDana;
                    break;
                case 6:
                    adresat.adres = pojedynczaDana;
                    break;

                }
            nrLinii++;
        }
    adresaci.push_back(adresat);

    return adresaci;
}

int wczytajPlikZDanymiAdresatow (vector <Adresat> &adresaci)
{

    int idAdresata = 0;
    vector <string> pojedynczyKontakt;
    string liniaTekstu;
    int nrLinii = 1;
    int iloscOsob = 0;
    fstream plikDoOdczytu;
    string ostatniaOsobaWPliku = " ";

    plikDoOdczytu.open("ksiazkaZAdresami.txt", ios::in);

    if (plikDoOdczytu.good() == true)
        {
            while (getline(plikDoOdczytu, liniaTekstu))
                {
                    adresaci = rozdzielDaneAdresatowNaPojedynczeDane(adresaci, liniaTekstu, '|');
                }

        }
    plikDoOdczytu.close();

}

void usunAdresata ()
{
    int idUsuwanejOsoby;
    char potwierdzenieWyboru;
    cout << "Podaj id adresata, ktorego chcesz usunac: ";
    cin.sync();
    cin >> idUsuwanejOsoby;
    cin.sync();
    vector <Adresat> ::iterator itr = adresaci.begin();
    for (itr; itr != adresaci.end(); ++itr)
        {
            if (itr->idAdresata == idUsuwanejOsoby)
                {
                    cout << itr->idAdresata << "|" << itr->imie << "|" << itr->nazwisko << "|" << itr->nrTel << "|" << itr->email << "|" << itr->adres << endl;
                    Sleep(1000);
                    cout << "Czy na pewno chcesz usunac tego adresata? Kliknij t: " << endl;
                    cin >> potwierdzenieWyboru;
                    if (potwierdzenieWyboru == 't')
                        {
                            adresaci.erase(itr);
                            //break;
                            cout << "Kontakt zostal usuniety";
                            Sleep(1500);
                            //zapisac zmiany do pliku
                            fstream plikPoUsunieciuOsoby;
                            plikPoUsunieciuOsoby.open("KsiazkaZAdresami.txt", ios::out | ios::trunc);
                            if (plikPoUsunieciuOsoby.good())  //plik poprawny
                                {
                                    for (itr; itr != adresaci.end(); itr++)
                                        {
                                            plikPoUsunieciuOsoby << itr->idAdresata << "|" << itr->imie << "|" << itr->nazwisko << "|" << itr->nrTel << "|" << itr->email << "|" << itr->adres << endl;

                                        }
                                        plikPoUsunieciuOsoby.close();
                                }
                                adresaci.clear(); // czyszcze wektor aby rozmiar wektora byl 0
                        }
                    else if (potwierdzenieWyboru != 't')
                        {
                        cout << "Blad! Wcisnales zly klawisz!";
                    }
                     break;

                }

        }

}

int main()
{

    int liczbaZapisanychOsob = 0;
    char twojWybor;

    char wyborWMenu;
    char checStworzeniaPliku;
    int iloscOsob = 0;

    //tworzenie pliku
    ifstream plik;
    plik.open("KsiazkaZAdresami.txt");
    if (plik)   //sprawdzam czy plik istnieje
        {
            cout << "Plik istnieje";
        }
    else     //jesli nie istnieeje, stwarzam nowy
        {
            cout << "Plik nie istnieje, jesli chccesz stworzyc nowy plik, klinkij n" << endl;
            cin >> checStworzeniaPliku;

            string nowyPlik;
            nowyPlik = "KsiazkaZAdresami.txt";
            ofstream plikDoStworzenia(nowyPlik.c_str());
        }

    liczbaZapisanychOsob = wczytajPlikZDanymiAdresatow (adresaci);

    while (1)
        {
            system("cls");
            cout << "Witaj w ksiazce adresowej!" << endl;
            cout << "Wybierz opcje: " << endl;
            cout << "1. Dodaj adresata" << endl;
            cout << "5. Usun adresata" << endl;

            cin >> twojWybor;

            if (twojWybor == '1')
                {
                    dodajOsobeDoKsiazki(liczbaZapisanychOsob);
                }

            else if (twojWybor == '5')
                {
                    usunAdresata();
                }
         
        }
    return 0;
}

 

komentarz 18 sierpnia 2021 przez Oscar Nałogowiec (29,320 p.)
edycja 18 sierpnia 2021 przez Oscar

Metoda erase pewnie działa zupełnie poprawnie, ale widze dziwną sekwencje działan:

1. W linii 159 usuwasz znaleziony element.

2. W linii 168 próbujesz dokończyć pętle zapisując elementy.

W tym jest błąd programistyczny - erase powoduje, że iterator staje się nieważny (wskazuje na usunięty element) oraz błąd logiczny - po co usuwać element skoro potem zapisujesz pozostałe elementy (tak chyba miało być) i czyścisz cały wektor.

I teraz poprawka zależy jaki miał być efekt. Jeśli chcesz usunąć tylko jeden element to początek pętli zapisywania musi jeszcze raz inicjować iterator na początek wektora (begin()).

Jeśli chcesz usunąć wszystko przed pasującym elementem (i ten element) to nie musisz nic w tym momencie usuwać  tylko zinkrementować itr i zapisać resztę.

I tak zaraz wszystko kasujesz.

 

PS. w linii 177 chyba powinno być 'n' ?

komentarz 18 sierpnia 2021 przez magda_19 Gaduła (3,080 p.)
Mój zamiar jest taki aby usunąć tylko podany element, czyli jeśli wpiszę idUsuwanejOsoby = 2, to chcę aby tylko ten element był usunięty. W którym miejscu powinnam zainicjować iterator?

Linia 168 miała zapisywać plik bez usuniętego elementu, taka jakby aktualizacja

 

PS. Powinno być 't', spójrz na linię 155
komentarz 18 sierpnia 2021 przez Oscar Nałogowiec (29,320 p.)
edycja 18 sierpnia 2021 przez Oscar

Linia 168:

                                    for (itr = adresaci.begin(); itr != adresaci.end(); ++itr)

Co prawda ładniej byłoby użyć innej zmiennej jako 'licznik' pętli. Ale i tak zewnętrzna pętla nie będzie kontynuowana, wiec to tylko kwestia estetyki.

A druga sprawa:

                    cout << "Czy na pewno chcesz usunac tego adresata? Kliknij t: " << endl;
                    cin >> potwierdzenieWyboru;
                    if (potwierdzenieWyboru == 't')
                        {
                          ....
                        }
                    else if (potwierdzenieWyboru != 't')
                        {
                        cout << "Blad! Wcisnales zly klawisz!";
                    }

Tak lepiej widać o co chodzi.

 

Jeszcze jedno PS. Używaj istream i ostream (i pochodne np ifstream i ofstream)  odpowiednio do tego czy czytasz czy piszesz. Wtedy otwieranie jest prostsze i kompilator od razu wykryje że próbujesz robić złe operacje. Sam (f)stream to potrzebny jest do skomplikowanych operacji modyfikacji pliku.

komentarz 18 sierpnia 2021 przez magda_19 Gaduła (3,080 p.)
Dzięki, ale nadal nie wiem jak mam ustawić iterator aby usuwał to, co chcę
komentarz 18 sierpnia 2021 przez Oscar Nałogowiec (29,320 p.)
edycja 18 sierpnia 2021 przez Oscar

Samo usuwanie wygląda na poprawne, problemem był późniejszy zapis wektora "od środka".

Tak swoją drogą to erase() owszem psuje iterator, ale jednocześnie zwraca wartość skorygowanego iteratora. Oczywiście wtedy już nie trzeba go inkrementować. Można więc napisać taką pętle:

for (auto it = collection.begin(); it != collection.end(); )  /* tu bez inkrementacji */
{
    /* jakas funkcja sprawdzajaca czy dany element trzeba usunac, moze np. pytac uzytkownika */
    if (doUsuniecia(it))   
        it = collection.erase(it);
    else
        ++it;
}

Takie coś zadziała i z wektorem i z listą.

komentarz 19 sierpnia 2021 przez magda_19 Gaduła (3,080 p.)

Dzięki za wskazówkę. Ja jednak wymyśliłam coś takiego:

Miałeś rację, za dużo było tych pętli w pętli głównej

void zapiszPoUsunieciu (vector <Adresat> adresaci)
{
    vector <Adresat> ::iterator itr = adresaci.begin();
    fstream plikPoUsunieciuOsoby;
    plikPoUsunieciuOsoby.open("KsiazkaZAdresami.txt", ios::out | ios::trunc);
    if (plikPoUsunieciuOsoby.good())  //plik poprawny
        {
            for (itr; itr != adresaci.end(); itr++)
                {
                    plikPoUsunieciuOsoby << itr->idAdresata << "|" << itr->imie << "|" << itr->nazwisko << "|" << itr->nrTel << "|" << itr->email << "|" << itr->adres << endl;

                }
            plikPoUsunieciuOsoby.close();
        }
    adresaci.clear();

}

void usunAdresata ()
{
    int idUsuwanejOsoby;
    char potwierdzenieWyboru;
    cout << "Podaj id adresata, ktorego chcesz usunac: ";
    cin.sync();
    cin >> idUsuwanejOsoby;
    cin.sync();
    vector <Adresat> ::iterator itr = adresaci.begin();
    for (itr; itr != adresaci.end(); ++itr)
        {
            if (itr->idAdresata == idUsuwanejOsoby)
                {
                    cout << itr->idAdresata << "|" << itr->imie << "|" << itr->nazwisko << "|" << itr->nrTel << "|" << itr->email << "|" << itr->adres << endl;
                    Sleep(1000);
                    cout << "Czy na pewno chcesz usunac tego adresata? Kliknij t: " << endl;
                    cin >> potwierdzenieWyboru;
                    if (potwierdzenieWyboru == 't')
                        {
                            adresaci.erase(itr);
                            //break;
                            cout << "Kontakt zostal usuniety";
                            Sleep(1500);
                            //zapisac zmiany do pliku
                           zapiszPoUsunieciu(adresaci);

                        }
                    else if (potwierdzenieWyboru != 't')
                        {
                        cout << "Blad! Wcisnales zly klawisz!";
                    }
                     break;

                }

        }

}

 

Zaloguj lub zarejestruj się, aby odpowiedzieć na to pytanie.

Podobne pytania

0 głosów
0 odpowiedzi 591 wizyt
pytanie zadane 26 lipca 2021 w C i C++ przez hicodyn Początkujący (420 p.)
0 głosów
0 odpowiedzi 543 wizyt
pytanie zadane 12 września 2020 w C i C++ przez magda_19 Gaduła (3,080 p.)
0 głosów
1 odpowiedź 5,768 wizyt
pytanie zadane 19 grudnia 2019 w C i C++ przez New_programmer Początkujący (340 p.)

92,669 zapytań

141,567 odpowiedzi

320,037 komentarzy

62,034 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...