Wczytanie tekstu z pliku do tablicy

0

Witajcie. Piszę aplikację quiz. W sumie mam ją już ukończoną, ale bardzo amatorsko. Aplikacja napisana jest w C++ Builder. Chodzi mi o wczytywanie pytań i odpowiedzi do programu. Aktualnie wykonuje to tak:

void __fastcall TForm1::Wczytaj()
{
       fstream plik;
       plik.open("pyt.txt", ios::in);

       if(plik.good() == false)
       {
              ShowMessage("Błąd! Nie udało się wczytac pliku!");
              exit(0);
       }


       int nr_linii = (nr_pytania - 1) * 5 + 1;
       int aktualny_nr = 1;

       while(getline (plik, Linia))
       {
              if(aktualny_nr == nr_linii) Tresc = Linia;
              if(aktualny_nr == nr_linii + 1) A = Linia;
              if(aktualny_nr == nr_linii + 2) B = Linia;
              if(aktualny_nr == nr_linii + 3) C = Linia;
              if(aktualny_nr == nr_linii + 4) Poprawna = Linia;
              aktualny_nr++;
       }
       plik.close();
       Wypisz();
}

screenshot-20171109202001.png

Czyli za każdym kliknięciem przycisku "Następne" wykonuje się ponownie wczytanie danych z pliku. Ja chcę, aby to zrobić tak, że w aplikacji użytkownik sam wskazuje plik, który ma zostać wczytany i wczytuje go do tablicy całościowo od razu. Czy jest taka możliwość?

0

Klasa TForm2

class TForm2 : public TForm
{
     __published:

     public:
       string Linia, Tresc, A, B, C, Poprawna, Odpowiedz;
       int nr_pytania, punkt, czas;
       vector < string > tab;

       void __fastcall Wczytaj(); 
       void __fastcall Wypisz(int nr); 
       void __fastcall Sprawdz();  
       void __fastcall Zapisz(); 
       void __fastcall LiczCzas();

       __fastcall TForm2();

};
void __fastcall TForm2::Wczytaj()
{
       if (Form1 -> OpenDialog1 -> Execute())
        {
                ifstream plik (Form1 -> OpenDialog1 -> FileName.c_str());
                if (plik.good() == false)
                {
                        ShowMessage("Błąd. Wskaż prawidłowy plik!");
                }
                else {
                        while (getline(plik, Linia)) tab.push_back(Linia);
                        plik.close();
                        Form1 -> Tekst_BladNazwisko -> Caption = "Plik wczytano poprawnie!";
                        Form1 -> Przycisk_Start -> Enabled = true;
                }
                
        }
}

W Form1 mam wszystkie przyciski itp. (elementy wizualne)
Po skompilowaniu programu, wczytaniu pliku wywala błąd:
Bez tytułu.jpg

i otwiera się debugger z podświetloną tą linijką:

  bool empty() const { return this->_M_start == this->_M_finish; }   

Zauważyłem, że jeżeli jest wszystko w jednej klasie to nie ma żadnych problemów!

0

Zgaduję, że odwołujesz się do tab w chwili, gdy obiekt klasy TForm2 już nie istnieje.

0
0x666 napisał(a):

Zgaduję, że odwołujesz się do tab w chwili, gdy obiekt klasy TForm2 już nie istnieje.

Dlaczego miałby przestać istnieć?

0

Jak pisałem - zgaduje ;) Z kodu, który podałeś, niewiele da się wyciągnąć, jeśli chodzi o przyczynę błędu.

W Form1 mam wszystkie przyciski itp. (elementy wizualne)

Jeśli TForm1 jest główną formą, czym jest TForm2? Samo wywołanie TForm2::Wczytaj() powoduje błąd? Jak tworzysz obiekt klasy TForm2?

0
0x666 napisał(a):

Jak pisałem - zgaduje ;) Z kodu, który podałeś, niewiele da się wyciągnąć, jeśli chodzi o przyczynę błędu.

W Form1 mam wszystkie przyciski itp. (elementy wizualne)

Jeśli TForm1 jest główną formą, czym jest TForm2? Samo wywołanie TForm2::Wczytaj() powoduje błąd? Jak tworzysz obiekt klasy TForm2?

https://pastebin.com/059ZUtRw

Błąd powoduje prawdopodobnie ta linijka:

while (getline(plik, Linia)) tab.push_back(Linia);

Ale nie było z nią problemów kiedy istniała tylko jedna klasa.

0

Ta linia jest jak najbardziej ok. Nie widzę, żeby Form2 było tworzone (cały czas upieram się, że obiekt klasy TForm2 nie istnieje w czasie wywołania metody). Sprawdź, jaka jest wartość wskaźnika this wewnątrz TForm2::Wczytaj().

Jaki jest sens klasy TForm2, jeśli wszędzie odwołujesz się do Form1?

0
0x666 napisał(a):

Ta linia jest jak najbardziej ok. Nie widzę, żeby Form2 było tworzone (cały czas upieram się, że obiekt klasy TForm2 nie istnieje w czasie wywołania metody). Sprawdź, jaka jest wartość wskaźnika this wewnątrz TForm2::Wczytaj().

Jaki jest sens klasy TForm2, jeśli wszędzie odwołujesz się do Form1?

Więc jak stworzyć Form2?
Co do sensu klasy TForm2. Robię projekt na uczelnie. Kazał jak najwięcej klas, a nie wiem jak to zrobić bardziej estetyczniej. Oczywiście klasę TForm2 podziele jeszcze na dwie inne. na razie walczę z tym błędem.

0

Po pierwsze napisz nową klasę opisującą pytania.
Zgodnie z wzorcem MVC (Model View Controller), masz napisać klasę modelu danych, który przechowuje te dane.
TForm1 to twój kontroler, a widokiem są elementy UI-a, które wyklikałeś.

I jeszcze: https://4programmers.net/Forum/C_i_C++/300163-pytanie_o_strukture_danych_w_pliku_txt?p=1422976#id1422976 (pewnie kolega).

0
MarekR22 napisał(a):

Po pierwsze napisz nową klasę opisującą pytania.

Ale to mi nic nie da, bo i tak muszę się odwołać do TForm1, aby wskazać OpenDialog1.

Jak sprawdzić ten this?

0

Jak sprawdzić ten this?

Debuggerem, albo nawet massageboxem (wyświetlasz zawartość AnsiString().sprintf("%p", this)).

aby wskazać OpenDialog1.

A po co? W onclicku przycisku odpalasz dialoga i ścieżkę podajesz w parametrze metody wczytaj().

0

W ogóle nie rozumiem co wy do mnie mówicie. :|
To musi być coś popierdzielone z tymi klasami, ponieważ wkleiłem to do pierwszej klasy TForm1

void __fastcall TForm1::Przycisk_WczytajClick(TObject *Sender)
{
        string Linia;
        if (OpenDialog1 -> Execute())
        {
                ifstream plik (OpenDialog1 -> FileName.c_str());
                if (plik.good() == false)
                {
                        ShowMessage("Błąd. Wskaż prawidłowy plik!");
                }
                else {
                        while (getline(plik, Linia)) tab.push_back(Linia);
                        plik.close();
                        Tekst_BladNazwisko -> Caption = "Plik wczytano poprawnie!";
                        Przycisk_Start -> Enabled = true;
                }
        }
}

oraz w publicu klasy dodałem

vector < string > tab;

I wczytało plik bez żadnych problemów...

Ale ja chcę mieć wczytywanie w drugiej klasie, a w tej same przyciski...

0
Karpiusz napisał(a):

Po skompilowaniu programu, wczytaniu pliku wywala błąd:
Bez tytułu.jpg

i otwiera się debugger z podświetloną tą linijką:

  bool empty() const { return this->_M_start == this->_M_finish; }   

Zauważyłem, że jeżeli jest wszystko w jednej klasie to nie ma żadnych problemów!

To jest linijka z STL-a. Żeby ustalić co jest nie tak, to:

  1. Odtwórz crash
  2. jak debugger zatrzyma się na tej linijce, rozejrzyj się po UI IDE, powinieneś znaleźć widok "Call stack"
  3. "Call stack" zawiera listę funkcji, które wywołały bieżącą funkcję
  4. Klikaj kolejne pozycje, aż osiągniesz swój kod.
  5. Podejrzyj zmienne w okolicach swojego kodu i zastanów, się czy jakiś wskaźnik nie wskazuje na nieprawidłową wartość.
0

Inaczej. Ustaw breakpointa na linii Form2 -> Wczytaj();, wywołaj zdarzenie z tą linią i kiedy program zatrzyma się na niej, podejrzyj, jaką wartość ma wskaźnik Form2.

I wczytało plik bez żadnych problemów...

Dlatego że obiekt tej klasy istnieje w pamięci, a obiekt wskazywany przez wskaźnik Form2 najprawdopodobniej nie. Sprawdź ten wskaźnik...

0

Kolego jak wyżej koledzy wspomnieli o MVC. Poczytaj o tym wzorcu są przykłady na necie. To jedno
Napisz testy jednostkowe w gtest(gmock) do softu. Unikniesz pewnych problemów. Wpisz w google TDD gtest.

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