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

[CR] C++ (#7): Pliki tekstowe. Zapis i odczyt z pliku tekstowego

VPS Starter Arubacloud
+4 głosów
15,049 wizyt
pytanie zadane 7 kwietnia 2016 w Nasze poradniki przez Mirosław Zelent Nałogowiec (34,750 p.)

CR = Code Review. O co chodzi? Zajrzyj tutaj
Pełna lista wszystkich Code Review? Zajrzyj tutaj

https://www.youtube.com/watch?v=h2Taf16gQDI

Kod z odcinka - zapis wizytówki:

#include <iostream>
#include <fstream>

using namespace std;

string imie, nazwisko;
int nr_tel;

int main()
{
    cout << "Podaj imie: ";         cin>>imie;
    cout << "Podaj nazwisko: ";     cin>>nazwisko;
    cout << "Podaj nr telefonu: ";  cin>>nr_tel;

    fstream plik;
    plik.open("wizytowka.txt",ios::out);

    plik<<imie<<endl;
    plik<<nazwisko<<endl;
    plik<<nr_tel<<endl;

    plik.close();

    return 0;
}

Kod z odcinka - zapis (dopisuj do pliku):

#include <iostream>
#include <fstream>

using namespace std;

string imie, nazwisko;
int nr_tel;

int main()
{
    cout << "Podaj imie: ";         cin>>imie;
    cout << "Podaj nazwisko: ";     cin>>nazwisko;
    cout << "Podaj nr telefonu: ";  cin>>nr_tel;

    fstream plik;
    plik.open("wizytowka.txt",ios::out | ios::app);

    plik<<imie<<endl;
    plik<<nazwisko<<endl;
    plik<<nr_tel<<endl;

    plik.close();

    return 0;
}

Kod z odcinka - odczyt wizytówki:

#include <iostream>
#include <fstream>
#include <cstdlib>

using namespace std;

string imie, nazwisko;
int nr_tel;

int main()
{

    string linia;
    int nr_linii=1;

    fstream plik;
    plik.open("wizytowka.txt", ios::in);

    if(plik.good()==false) cout<<"Nie mozna otworzyc pliku!";

    while (getline(plik, linia))
    {
        switch (nr_linii)
        {
            case 1: imie=linia; break;
            case 2: nazwisko=linia; break;
            case 3: nr_tel=atoi(linia.c_str()); break;
        }
        nr_linii++;
    }

    plik.close();

    cout<<"imie: "<<imie<<endl;
    cout<<"nazwisko: "<<nazwisko<<endl;
    cout<<"telefon: "<<nr_tel<<endl;

    return 0;
}

Kod z odcinka - quiz:

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <algorithm>
#include <string>

using namespace std;

string temat, nick;
string tresc[5];
string odpA[5], odpB[5], odpC[5], odpD[5];
string poprawna[5];
string odpowiedz;
int punkty=0;

int main()
{
    int nr_linii=1;
    string linia;
    int nr_pytania=0;

    fstream plik;
    plik.open("quiz.txt",ios::in);

    if (plik.good()==false)
    {
        cout<<"Nie udalo sie otworzyc pliku!";
        exit(0);
    }

    while(getline(plik,linia))
    {
        switch(nr_linii)
        {
            case 1: temat=linia;                     break;
            case 2: nick=linia;                      break;
            case 3: tresc[nr_pytania] = linia;       break;
            case 4: odpA[nr_pytania] = linia;        break;
            case 5: odpB[nr_pytania] = linia;        break;
            case 6: odpC[nr_pytania] = linia;        break;
            case 7: odpD[nr_pytania] = linia;        break;
            case 8: poprawna[nr_pytania] = linia;    break;
        }

        if (nr_linii==8) {nr_linii=2; nr_pytania++;}
        nr_linii++;
    }

    plik.close();

    for (int i=0; i<=4; i++)
    {
        cout<<endl<<tresc[i]<<endl;
        cout<<"A. "<<odpA[i]<<endl;
        cout<<"B. "<<odpB[i]<<endl;
        cout<<"C. "<<odpC[i]<<endl;
        cout<<"D. "<<odpD[i]<<endl;

        cout<<"Twoja odpowiedz: ";
        cin>>odpowiedz;

        transform(odpowiedz.begin(), odpowiedz.end(), odpowiedz.begin(), ::tolower);

        if (odpowiedz==poprawna[i])
        {
            cout<<"Dobrze! Zdobywasz punkt!"<<endl;
            punkty++;
        }
        else cout<<"Zle! Brak punktu! Poprawna odpowiedz: "<<poprawna[i]<<endl;

    }

    cout<<"Koniec quizu. Zdobyte punkty: "<<punkty;

    return 0;
}

Kod z odcinka - odczyt z użyciem EOF:

#include <iostream>
#include <fstream>
#include <cstdlib>

using namespace std;

float liczby[100];

int main()
{

    string linia;
    int nr_linii=1;

    fstream plik;
    plik.open("pomiary.txt", ios::in);

    if(plik.good()==false) cout<<"Nie mozna otworzyc pliku!";

    int licznik=0;

    while(!plik.eof())
    {
        getline(plik, linia);
        liczby[licznik]=atof(linia.c_str());
        licznik++;
    }

    plik.close();

    cout<<"Ilosc pomiarow: "<<licznik<<endl<<endl;

    for (int i=0; i<licznik; i++)
    {
        cout<<liczby[i]<<endl;
    }

    return 0;
}

Paczka z odcinka: POBIERZ​

komentarz 28 lutego 2019 przez <span>ko Nowicjusz (100 p.)
Dobra, pomocy - absolutnie nie mam pojęcia co źle robię.

Sprawdziłem każdą linijkę - wszystko jest poprawnie. W napływie desperacji przekopiowałem nawet powyższy kod.

Po naciśnięciu F9 aplikacja się włącza, wpisuje co mam wpisać, aplikacja się zamyka i to tyle. No i właśnie tu jest problem.

Nie tworzy się plik z zapisaną wizytówką. Nawet, jeśli sam utworzę go wcześnie we właściwym folderze to dane wpisane w konsoli nie zapisują się do niego.
Jak żyć? Co robić?

Proszę o pomoc

 

 

#include <iostream>
#include <fstream>

using namespace std;

string imie, nazwisko;
int nr_tel;

int main()
{
    cout << "Podaj Imie: ";         cin>>imie;
    cout << "Podaj Nazwisko: ";     cin>>nazwisko;
    cout << "Podaj numer telefonu: ";  cin>>nr_tel;

    fstream plik;
    plik.open("wizytowka.txt",ios::out);
    plik<<imie<<endl;
    plik<<nazwisko<<endl;
    plik<<nr_tel<<endl;

    plik.close();

    return 0;
}
komentarz 28 stycznia 2022 przez ravor Nowicjusz (100 p.)
Jeżeli używasz jakiegokolwiek antywirusa, prawdopodobnie musisz utworzyć wyjątek na plik (najlepiej folder z całym programem), tak aby go nie odczytywał jako zagrożenie dla systemu.

2 odpowiedzi

+2 głosów
odpowiedź 7 kwietnia 2016 przez niezalogowany

Kiedyś rozpocząłem w miarę dokładną analizę tego odcinka i pozwolę sobie wkleić tutaj moje spostrzeżenia (należy zauważyć że są to moje notatki, więc brakuje odpowiedniego "wygładzenia"):

  • [3:21] Niekoniecznie trzeba go zamknąć (przy korzystaniu z fstream) ponieważ i tak zostanie on zamknięty w momencie niszczenia obiektu tej klasy [fstream::close].
  • [5:28] Odczuwam brak wyjaśnienia różnicy pomiędzy ścieżkami względnymi, bezwzględnymi.
  • [10:48] Niezwykle bezsensowne użycie switcha, pisałem o tym nieco na [forum]. Pomijam już wyjątkowo mało czytelny sposób zapisu case'ów.
  • [12:25] [Quiz - źródło problemów]
  • [16:12] Rly? Bardzo brzydki kod, można było spokojnie obejść się bez switcha (który został wepchnięty chyba na siłę), także to nie jest sprytne rozwiązanie, to jest zbyt mocno przekombinowane.Przy okazji: mamy wymieszanie camelCase (odpA) z snake_case. Nieładnie, trzymajmy się jednej konwencji. Poniżej widzimy kod, który jest zdecydowanie "sprytniejszy" od tego co widzimy na filmie (zakładamy, że dane są prawidłowe):
/* stosuje nazewnictwo z filmu */
int nr_pytania = 0;
while (!plik.eof())
{
  getline(plik, temat);
  getline(plik, nick);
  getline(plik, tresc);

  /* tutaj aż prosi się wrzucić odpowiedzi do tablicy i wczytać w pętli */
  getline(plik, odpA[nr_pytania]);
  getline(plik, odpB[nr_pytania]);
  getline(plik, odpC[nr_pytania]);
  getline(plik, odpD[nr_pytania]);  
  ++nr_pytania;
}
  • [19:47] 4 if'y? Można to zrobić na 2 dla pojedynczych znaków (zakres dolny i górny) + 1 warunek aby przekształcić kod do konwersji liter na małe dla całego stringa.
komentarz 7 kwietnia 2016 przez adrian17 Ekspert (344,100 p.)
Jeszcze bym wspomniał użycie konstruktora z argumentami zamiast open().
komentarz 8 kwietnia 2016 przez maly Nałogowiec (37,190 p.)

Poniżej widzimy kod, który jest zdecydowanie "sprytniejszy" od tego co widzimy na filmie (zakładamy, że dane są prawidłowe):

Rly? Skoro Twój kod nie jest równoważny funkcjonalnie z kodem Mirka nie można go uznać za alternatywę a już tym bardziej za lepszą, poza tym "sprytniejszym" kodem bym go nie nazwał.

komentarz 8 kwietnia 2016 przez niezalogowany
W oryginalnym kodzie na spokojnie można uznać, że dane są prawidłowe (kto by wprowadzał w 20min odcinku dokładną weryfikację danych, która de facto powina wymagać także nieco innego samego zapisu wewnątrz pliku), konstrukcja przedstawiona przeze mnie jest o wiele prostsza, w dodatku nie posiada zbędnego w tym przypadku sprawdzania numeru linii, używa mniej zmiennych, nie wykonuje tylu skoków.

Także mamy realizację dokładnie tego samego zadania w nieco oszczędniejszy sposób przy tych samych założeniach, w dodatku kod jest nieco bardziej elastyczny. Nie mówię, że to jest idealny sposób (bo nie jest), ale jak na pierwszą lekcję z podstaw pracy na plikach jest moim zdaniem lepszy.
komentarz 8 kwietnia 2016 przez maly Nałogowiec (37,190 p.)
Skrytykowałeś konkretny kod, zmieniłeś założenia/wymagania, na ich podstawie napisałeś swój kod i stwierdziłeś że jest lepszy. No, niewiem.
komentarz 8 kwietnia 2016 przez niezalogowany
Ok, powiedz czego mój kod nie robi oraz jakie założenia zmieniłem.

Pragnę zauważyć, że w razie braku niektórych linii w pliku kod jest tak samo podatny na błędy. Jedyną zmianą jaka jest pomiędzy propozycją MZ, a moją to pozbycie się niepotrzebnych w tym przypadku ifów (switcha) oraz zmiennej. Podaj konkrety
komentarz 11 kwietnia 2016 przez maly Nałogowiec (37,190 p.)

Masz rację, zabezpieczenie jest niepełnosprawne jednak pogłebienie jego kalectwa nie nazwałbym ulepszeniem.
Twierdzisz że kod jest bardziej elastyczny, dla mnie elastyczność to możliwość rozwoju i modyfikacji którą uniemożliwiłeś.
Jaką wartość edukacyjną ma kod z którym nic nie da się zrobić poza wklejeniem kolejnej lini?

Podałem w skrócie co miałem na myśli a teraz pozwól że poszukam sobie na forum czegoś innego do shejtowania skrytykowania.
Dziękuję za uwagę i pozdrawiam.

komentarz 11 kwietnia 2016 przez niezalogowany
@maly kod, który ja podałem jest czymś czego standardowo się uczy przy obsłudze plików bez wprowadzania weryfikacji danych, problem z tym kodem jest taki, że ludzie po tej lekcji piszą identyczny kod na switchach nawet jeżeli mają tylko jeden zestaw danych / 1 schemat danych, np: login:pass w nowych liniach
komentarz 11 kwietnia 2016 przez maly Nałogowiec (37,190 p.)

problem z tym kodem jest taki, że ludzie po tej lekcji piszą identyczny kod

Nie, problem jest taki że ludzie bezmyślnie kopiują kod a Ty chcesz im to jeszcze bardziej ułatwić, idąc tym tokiem myślenia masz rację Twój kod jest lepszy, ja jednak w mojej bezgranicznej naiwnosci wolę wierzyć że kod z kursu zostanie kompleksowo zmolestowany i zgwałcony ze wszyskich stron i na wszystkie możliwe sposoby a dopiero potem przejdzie do kolejnej ofiary części.

+1 głos
odpowiedź 7 kwietnia 2016 przez niezalogowany

Uwaga co do:

while(!plik.eof())

Nie powinno się tego używać. Jeśli plik byłby zakończony znakiem nowej linii to albo ostatnia wartość zostanie odczytana 2 razy albo jeśli masz włączony w kompilatorze C++11 zostanie odczytana wartość 0.

Przykład był też trochę bezsensowny. Jeśli wiemy, że w pliku są liczby to możemy zrobić tak:

#include <iostream>
#include <fstream>
#include <vector>

int main()
{
    std::vector<float> numbers;  	
    std::fstream file("pomiary.txt", std::ios::in);
    if(file.fail()) 
	   std::cerr<<"Nie udalo sie otworzyc pliku\n";
    float temp;
    while(file>>temp)
    {
	    	numbers.push_back(temp);
    }
 
    file.close();
 
    for (int i=0; i < numbers.size(); ++i)
    {
	    std::cout<<numbers[i]<<std::endl;
    }
 
    return 0;
}

A jeśli nie mamy pewności że w pliku są same liczby to tak:

#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>

int main()
{
    std::vector<float> numbers;  	
    std::fstream file("pomiary.txt", std::ios::in);
    if(file.fail()) 
	   std::cerr<<"Nie udalo sie otworzyc pliku\n";
    float temp;
    std::string line; 
    while(file>>line)
    {
	    std::istringstream iss(line);
	    while(iss>>temp)
	    	numbers.push_back(temp);
    }
 
    file.close();
 
    for (int i=0; i < numbers.size(); ++i)
    {
	    std::cout<<numbers[i]<<std::endl;
    }
 
    return 0;
}

 

3
komentarz 7 kwietnia 2016 przez niezalogowany
Myślę, że twoje rozumowanie jest nie do końca poprawne (odnośnie eof), mianowicie poprawny plik powinien mieć tylko jeden znak końca linii, a w tym przykładzie to właśnie zakładamy.

Jeżeli byłoby inaczej to powinieneś sprawdzać czy wczytywane dane są poprawne, czego już nie robisz, w dodatku twój kod bazuje na rzeczach, które nie były pokazane wcześniej i można je uznać za "zaawansowane" (vector), a to jest kurs podstawowy
komentarz 7 kwietnia 2016 przez niezalogowany
Mówiłem o pliku który ma 1 lub więcej znaków końca lini na końcu i sprawdza sie to co napisałem. Nie wiem czy dobrze zrozumiałem o co chodzi ci w drugim zdaniu ale nie musze tego sprawdzać bo >> ignoruje białe znaki.
komentarz 7 kwietnia 2016 przez niezalogowany
Ok, tylko że plik i tak powinien posiadać wyłącznie jeden EOF.

To co chciałem przekazać, to fakt że skoro sprawdzasz poprawność pliku pod kątem ilość EOF to aby być rzetelnym powinieneś również sprawdzić czy wczytane dane (liczby) są poprawne, tzn nie ma tam śmieciowych danych zamiast liczb.
komentarz 8 kwietnia 2016 przez niezalogowany
Drugi kod dodaje do vectora tylko liczby, a 1 jest w przypadku gdy mamy pewność że w pliku są tylko liczby.

Podobne pytania

0 głosów
1 odpowiedź 470 wizyt
+1 głos
1 odpowiedź 2,091 wizyt
0 głosów
0 odpowiedzi 477 wizyt
pytanie zadane 13 stycznia 2022 w C i C++ przez Nikso Nowicjusz (230 p.)

92,453 zapytań

141,262 odpowiedzi

319,086 komentarzy

61,854 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

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

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!

...