c++ problem z zapisem/odczytem plików binarnych

0

Witam,
Robię program w konsoli obsługujący pliki binarne. Kod poprawnie się kompiluje i zapisuje do pliku poprawnie aż do wyłączenia. Po ponownym uruchomieniu pojawiają się błędy, m. in. widzi wszystkie rekordy, lecz twierdzi, że każde pole jest puste; przy zapisywaniu kolejnych rekordów dopisuje je na koniec i dodatkowo podmienia jeden z "pustych" rekordów; przy wyświetlaniu rekordów czasem wyrzuca całą tabelę ASCII i wyrzuca raport o błędzie (wszystko to dopiero za drugim i kolejnym uruchomieniem)

Oto kod:

#include <iostream>
#include <cstdio>
#include <conio.h>
#include <string>
#include <fstream>
#include <windows.h>
using namespace std;

struct user {
           string login;
           string haslo;
    };

string PL(string znak) {
  for (unsigned i = 0; i < znak.length(); i++) {
    switch (znak[i]) {
      case 'ą': znak[i] = static_cast<char>(165); break;
      case 'ć': znak[i] = static_cast<char>(134); break;
      case 'ę': znak[i] = static_cast<char>(169); break;
      case 'ł': znak[i] = static_cast<char>(136); break;
      case 'ń': znak[i] = static_cast<char>(228); break;
      case 'ó': znak[i] = static_cast<char>(162); break;
      case 'ś': znak[i] = static_cast<char>(152); break;
      case 'ź': znak[i] = static_cast<char>(171); break;
      case 'ż': znak[i] = static_cast<char>(190); break;
      case 'Ą': znak[i] = static_cast<char>(164); break;
      case 'Ć': znak[i] = static_cast<char>(143); break;
      case 'Ę': znak[i] = static_cast<char>(168); break;
      case 'Ł': znak[i] = static_cast<char>(157); break;
      case 'Ń': znak[i] = static_cast<char>(227); break;
      case 'Ó': znak[i] = static_cast<char>(224); break;
      case 'Ś': znak[i] = static_cast<char>(151); break;
      case 'Ź': znak[i] = static_cast<char>(141); break;
      case 'Ż': znak[i] = static_cast<char>(189); break;
    }
  }
  return znak;
}

void maszyna(string tekst, int przerwa) {
     int len = tekst.length();
     for (int a=0;a<len;a++) {
         cout << tekst[a];
         Sleep(przerwa);
     }
}
bool first = true;
void r();
void l();
void s();

int main() {
    if (first) {
       cout << "///////////////////////////////////////////////////////////////////////////////" << endl
            << "////////////////////////////////// APLIKACJA  /////////////////////////////////" << endl
            << "///////////////////////////////////////////////////////////////////////////////" << endl;
    }
    first = false;
    Sleep(1000);
    cout << endl;
    M:
    maszyna(PL("Wybierz operację:\n(r)ejestracja,\n(l)ogowanie,\n(w)yjście,\n(s)pis użytkowników\n"), 20);
    cin.clear();
    cin.sync();
    char znak = getch();
    cin.clear();
    cin.sync();
    cout << endl;
    if (static_cast <int> (znak)==27) znak = 'w';
    switch (znak) {
           case 'r':
                r();
                break;
           case 'l':
                l();
                break;
           case 'w':
                maszyna(PL("Operacja: wyjście\n"), 20);
                Sleep(100);
                maszyna(PL("Zapraszamy ponownie"), 20);
                Sleep(2000);
                break;
           case 's':
                s();
                break;
           default:
                maszyna(PL("Nieprawidłowy klawisz\n\n"), 20);
                Sleep(1000);
                cout << endl;
                goto M;
                break;
    }
    return 0;
}

void r() {
    FILE * plik;
    user u1[2];
    user users[256];
    int ilosc = 0;
    int id = -1;
    string login;
    string haslo;
    bool blad;
    maszyna(PL("operacja: rejestracja"), 20);
    Sleep(100);
    if ((plik = fopen("users.bin","rb"))==NULL) {
       cout << endl << PL("Błąd przy otwieraniu pliku.") << endl;
       getch();
       return;
    }
    while (fread(&users[ilosc], sizeof(user), 1, plik)==1) ilosc++;
    fclose(plik);
    do {
       do {
          maszyna(PL("\nPodaj login: "), 20);
          cin >> login;
          blad = !cin.good();
          Sleep(100);
       } while (blad);
       cin.clear();
       cin.sync();
       for (int a=0;a<ilosc;a++) if (users[a].login==login) {blad = true; cout << endl << a<<" "<<users[a].login << PL(" - Podany login jest już zajęty. Wybierz inny") << endl; Sleep(100);}
    } while (blad);
    do {
       maszyna(PL("\nPodaj hasło: "), 20);
       char z;
       int i = 0;
       while (i!=13) {
             z = getch();
             i = static_cast <int> (z);
             if (i>=33 && i<=126) {
                haslo += z;
                cout << "*";
             }  else if (i==8&&haslo.length()>0) { //backspace
                haslo.erase(haslo.length()-1, 1);
                cout << "\b \b";
             }
       }
       cout << endl;
       blad = !cin.good();
    } while (blad);
       cin.clear();
       cin.sync();
    u1[0].login = login;
    u1[0].haslo = haslo;
    users[ilosc].login = login;
    users[ilosc].haslo = haslo;
    ilosc++;
    if ((plik = fopen("users.bin","w+b"))==NULL) {
       cout << endl << PL("Błąd przy otwieraniu pliku.") << endl;
       getch();
       return;
    }
    for (int i=0;i<ilosc;i++) fwrite(&users[i], sizeof(user), 1, plik);
    fclose(plik);
    cout << "Zapisano." << endl;
    cout << endl;
    main();
}

void l() {
     FILE * plik;
     user users[256];
     user u1[2];
     bool blad;
     char z;
     int i = 0;
     int id = -1;
     int ilosc = 0;
     string login = "", haslo = "";
     maszyna(PL("Operacja: logowanie"), 20);
     Sleep(100);
     do {
        maszyna(PL("\nPodaj login: "), 20);
        cin >> login;
        blad = !cin.good();
        Sleep(100);
     } while (blad);
        cin.clear();
        cin.sync();
     do {
        maszyna(PL("\nPodaj hasło: "), 20);
        char z;
        int i = 0;
        while (i!=13) {
              z = getch();
              i = static_cast <int> (z);
              if (i>=33 && i<=126) {
                 haslo += z;
                 cout << "*";
              }  else if (i==8&&haslo.length()>0) { //backspace
                 haslo.erase(haslo.length()-1, 1);
                 cout << "\b \b";
              }
        }
        cout << endl;
        blad = !cin.good();
     } while (blad);
        cin.clear();
        cin.sync();
     u1[0].login = login;
     u1[0].haslo = haslo;
     if ((plik = fopen("users.bin","rb"))==NULL) {
        cout << PL("Błąd przy otwieraniu pliku.") << endl;
        getch();
     }
     while (fread(&users[ilosc], sizeof(user), 1, plik)==1) ilosc++;
     for (int a=0;a<ilosc;a++) {
         if (users[a].login==u1[0].login && users[a].haslo==u1[0].haslo) id = a;
     }
     fclose(plik);
     if (id==-1) {
        cout << PL("Błędne dane.\n");
        Sleep(1000);
        cout << endl << endl;
        main();
     }
     cout << PL("Twój identyfikator to: ") << id << endl;
     //dalsze operacje
}
void s() {
     FILE * plik;
     if ((plik = fopen("users.bin","rb"))==NULL) {
        cout << PL("Błąd przy otwieraniu pliku.") << endl;
        getch();
        return;
     }
     int ilosc = 0;
     user u[12];
     while (fread(&u[ilosc], sizeof(user), 1, plik)==1) ilosc++;
     cout << "Total: "<<ilosc<<PL(" userów")<<endl;
     Sleep(1000);
     for (int a=0;a<ilosc;a++) cout << "id: " << a << "; login: " << u[a].login << "; haslo: " << u[a].haslo << endl;
     fclose(plik);
     main();
}

Z góry dziękuję za odpowiedź

0

#Nigdy, ale to nigdy sam nie wywołuj main'a (chyba, że będziesz już pro i będziesz wiedział po co)!
#goto jest bardzo złym stylem programowania, zastąp to pętlą
#zasadniczo brakuje ci pętli w main (patrz punkt 2) i dlatego zrobiłeś jej substytut (nie znając konsekwencji) przez wywołanie main.
#nazywanie funkcji jednoliterowo to proszenie się o dużego kopa w d..ę

0

Poprawiłem błędy, lecz problem został.

#include <iostream>
#include <cstdio>
#include <conio.h>
#include <string>
#include <fstream>
#include <windows.h>
using namespace std;

struct user {
           string login;
           string haslo;
    };

string PL(string znak) {
  for (unsigned i = 0; i < znak.length(); i++) {
    switch (znak[i]) {
      case 'ą': znak[i] = static_cast<char>(165); break;
      case 'ć': znak[i] = static_cast<char>(134); break;
      case 'ę': znak[i] = static_cast<char>(169); break;
      case 'ł': znak[i] = static_cast<char>(136); break;
      case 'ń': znak[i] = static_cast<char>(228); break;
      case 'ó': znak[i] = static_cast<char>(162); break;
      case 'ś': znak[i] = static_cast<char>(152); break;
      case 'ź': znak[i] = static_cast<char>(171); break;
      case 'ż': znak[i] = static_cast<char>(190); break;
      case 'Ą': znak[i] = static_cast<char>(164); break;
      case 'Ć': znak[i] = static_cast<char>(143); break;
      case 'Ę': znak[i] = static_cast<char>(168); break;
      case 'Ł': znak[i] = static_cast<char>(157); break;
      case 'Ń': znak[i] = static_cast<char>(227); break;
      case 'Ó': znak[i] = static_cast<char>(224); break;
      case 'Ś': znak[i] = static_cast<char>(151); break;
      case 'Ź': znak[i] = static_cast<char>(141); break;
      case 'Ż': znak[i] = static_cast<char>(189); break;
    }
  }
  return znak;
}

void maszyna(string tekst, int przerwa) {
     int len = tekst.length();
     for (int a=0;a<len;a++) {
         cout << tekst[a];
         Sleep(przerwa);
     }
}
void rejestruj();
void loguj();
void spis();
void menu();

int main() {
    cout << "///////////////////////////////////////////////////////////////////////////////" << endl
         << "////////////////////////////////// APLIKACJA  /////////////////////////////////" << endl
         << "///////////////////////////////////////////////////////////////////////////////" << endl;
    Sleep(1000);
    cout << endl;
    menu();
    return 0;
}
void menu() {
    maszyna(PL("Wybierz operację:\n(r)ejestracja,\n(l)ogowanie,\n(w)yjście,\n(s)pis użytkowników\n"), 20);
    cin.clear();
    cin.sync();
    char znak = getch();
    cin.clear();
    cin.sync();
    cout << endl;
    if (static_cast <int> (znak)==27) znak = 'w';
    switch (znak) {
           case 'r':
                rejestruj();
                break;
           case 'l':
                loguj();
                break;
           case 'w':
                maszyna(PL("Operacja: wyjście\n"), 20);
                Sleep(100);
                maszyna(PL("Zapraszamy ponownie"), 20);
                Sleep(2000);
                break;
           case 's':
                spis();
                break;
           default:
                maszyna(PL("Nieprawidłowy klawisz\n\n"), 20);
                Sleep(1000);
                cout << endl;
                menu();
                break;
    }
}

void rejestruj() {
    FILE * plik;
    user u1[2];
    user users[256];
    int ilosc = 0;
    int id = -1;
    string login;
    string haslo;
    bool blad;
    maszyna(PL("operacja: rejestracja"), 20);
    Sleep(100);
    if ((plik = fopen("users.bin","rb"))==NULL) {
       cout << endl << PL("Błąd przy otwieraniu pliku.") << endl;
       getch();
       return;
    }
    while (fread(&users[ilosc], sizeof(user), 1, plik)==1) ilosc++;
    fclose(plik);
    do {
       do {
          maszyna(PL("\nPodaj login: "), 20);
          cin >> login;
          blad = !cin.good();
          Sleep(100);
       } while (blad);
       cin.clear();
       cin.sync();
       for (int a=0;a<ilosc;a++) if (users[a].login==login) {blad = true; cout << endl << a<<" "<<users[a].login << PL(" - Podany login jest już zajęty. Wybierz inny") << endl; Sleep(100);}
    } while (blad);
    do {
       maszyna(PL("\nPodaj hasło: "), 20);
       char z;
       int i = 0;
       while (i!=13) {
             z = getch();
             i = static_cast <int> (z);
             if (i>=33 && i<=126) {
                haslo += z;
                cout << "*";
             }  else if (i==8&&haslo.length()>0) { //backspace
                haslo.erase(haslo.length()-1, 1);
                cout << "\b \b";
             }
       }
       cout << endl;
       blad = !cin.good();
    } while (blad);
       cin.clear();
       cin.sync();
    u1[0].login = login;
    u1[0].haslo = haslo;
    users[ilosc].login = login;
    users[ilosc].haslo = haslo;
    ilosc++;
    if ((plik = fopen("users.bin","w+b"))==NULL) {
       cout << endl << PL("Błąd przy otwieraniu pliku.") << endl;
       getch();
       return;
    }
    for (int i=0;i<ilosc;i++) fwrite(&users[i], sizeof(user), 1, plik);
    fclose(plik);
    cout << "Zapisano." << endl;
    cout << endl;
    menu();
}

void loguj() {
     FILE * plik;
     user users[256];
     user u1[2];
     bool blad;
     char z;
     int i = 0;
     int id = -1;
     int ilosc = 0;
     string login = "", haslo = "";
     maszyna(PL("Operacja: logowanie"), 20);
     Sleep(100);
     do {
        maszyna(PL("\nPodaj login: "), 20);
        cin >> login;
        blad = !cin.good();
        Sleep(100);
     } while (blad);
        cin.clear();
        cin.sync();
     do {
        maszyna(PL("\nPodaj hasło: "), 20);
        char z;
        int i = 0;
        while (i!=13) {
              z = getch();
              i = static_cast <int> (z);
              if (i>=33 && i<=126) {
                 haslo += z;
                 cout << "*";
              }  else if (i==8&&haslo.length()>0) { //backspace
                 haslo.erase(haslo.length()-1, 1);
                 cout << "\b \b";
              }
        }
        cout << endl;
        blad = !cin.good();
     } while (blad);
        cin.clear();
        cin.sync();
     u1[0].login = login;
     u1[0].haslo = haslo;
     if ((plik = fopen("users.bin","rb"))==NULL) {
        cout << PL("Błąd przy otwieraniu pliku.") << endl;
        getch();
     }
     while (fread(&users[ilosc], sizeof(user), 1, plik)==1) ilosc++;
     for (int a=0;a<ilosc;a++) {
         if (users[a].login==u1[0].login && users[a].haslo==u1[0].haslo) id = a;
     }
     fclose(plik);
     if (id==-1) {
        cout << PL("Błędne dane.\n");
        Sleep(1000);
        cout << endl << endl;
        menu();
     }
     cout << PL("Twój identyfikator to: ") << id << endl;
     //dalsze operacje
}
void spis() {
     FILE * plik;
     if ((plik = fopen("users.bin","rb"))==NULL) {
        cout << PL("Błąd przy otwieraniu pliku.") << endl;
        getch();
        return;
     }
     int ilosc = 0;
     user u[12];
     while (fread(&u[ilosc], sizeof(user), 1, plik)==1) ilosc++;
     cout << "Total: "<<ilosc<<PL(" userów")<<endl;
     Sleep(1000);
     for (int a=0;a<ilosc;a++) cout << "id: " << a << "; login: " << u[a].login << "; haslo: " << u[a].haslo << endl;
     fclose(plik);
     cout << endl;
     menu();
}
0

prosiłem byś wskazał miejsce wczytywania i zapisywania.
przykładowo znalazłem fopen("users.bin","rb") , które powtarza się aż 6 razy! Które wczytywanie pliku jest najważniejsze? WTF! nie powinieneś mieć na to jednej funkcji wczytaj dane?
Możesz mieć błąd w jednym z tych miejsc lub we wszystkich lub coś pośredniego.
To samo fopen("users.bin","w+b") powtarza się 2 razy.


Wiem w czym problem, zapisujesz binarnie std:string bezczelnie zapisując i odczytując ich zawartość. TAK NIE WOLNO. std:string zarządza pamięcią przeznaczoną na napis, więc tak naprawdę jest to tylko wskaźnik! Gdybyś miał tam **char login[20]; char haslo[20];** to wtedy taki zapis binarny byłby dozwolony, bo wszystkie dane znajdują się w jednym miejscu nie są to wskaźniki na pamięć!

uprość sobie życie i zapisuj to tekstowo jako CSS (login nie ma przecinka a reszt aż do końca linii może być hasłem) i jeśli już stosujesz STL (string) to używaj fstream (będzie łatwiej).

0

Dziękuję za odpowiedź, dzięki której udało mi się zrozumieć główne błędy. Przebudowałem kod i już działa.

Pozdrawiam,
kajak4Cpp

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