Program plikowy, plik typowany

0

Witam! Robię projekt do szkoły małą bazę danych, z wykorzystaniem listy jednokierunkowej. Próbuję wykorzystać do tego plik typowany.
Stworzyłem do tej pory coś takiego:
Unit1.

 
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Buttons, Unit2;

type
  TForm1 = class(TForm)
    StaticText1: TStaticText;
    StaticText2: TStaticText;
    StaticText3: TStaticText;
    StaticText4: TStaticText;
    StaticText5: TStaticText;
    StaticText6: TStaticText;
    Bevel1: TBevel;
    ListBox1: TListBox;
    ListBox2: TListBox;
    ListBox3: TListBox;
    ListBox4: TListBox;
    ListBox5: TListBox;
    ListBox6: TListBox;
    GroupBox1: TGroupBox;
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
    BitBtn3: TBitBtn;
    BitBtn4: TBitBtn;
    BitBtn5: TBitBtn;
    BitBtn6: TBitBtn;
    procedure BitBtn5Click(Sender: TObject);
    procedure BitBtn6Click(Sender: TObject);
    procedure BitBtn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
  Wskaznik = ^Baza;
  Baza = record
    Lp : String[10];
    Imie    : String[30];
    Nazwisko: String[30];
    Telefon : String[30];
    Adres   : String[30];
    Nastepny: Wskaznik;
    end;
  Plik_Bazodanowy = File of Baza;
var
  Form1: TForm1;
  Plik_Bazy: Plik_Bazodanowy;
  Pierwszy : Wskaznik;
  Ostatni  : Wskaznik;
  Nowy     : Wskaznik;
  db       : Baza;
  i        : Integer;
implementation

{$R *.dfm}

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
Form2.ShowModal;
end;

procedure TForm1.BitBtn5Click(Sender: TObject);
begin
  Pierwszy := Nil;
  Ostatni  := Nil;
  AssignFile(Plik_Bazy,'Plik_do_bazy.txt');
  if Not FileExists('Plik_do_bazy.txt') then
    begin
      ReWrite(Plik_Bazy)
    end
  else begin
    ReSet(Plik_Bazy);
  end;
  CloseFile(Plik_Bazy);
end;

procedure TForm1.BitBtn6Click(Sender: TObject);
begin
close;
end;
begin
  AssignFile(Plik_Bazy,'Plik_do_bazy.txt');
  if Not FileExists('Plik_do_bazy.txt') then
    begin
      ReWrite(Plik_Bazy)
    end
  else begin
    ReSet(Plik_Bazy);
    for i := 0 to FileSize(Plik_Bazy)-1 do
       begin
        New(Nowy);
        Read(Plik_Bazy, Nowy^);
        if Pierwszy = nil then
          begin
            Pierwszy:=Nowy;
            Ostatni :=Pierwszy;
          end;
          Ostatni^.Nastepny := Nowy;
          Ostatni:= Nowy;
      end;
      Nowy := Pierwszy;
      while Nowy <> nil do
      begin

          form1.Listbox1.Items.Add(Nowy^.Lp);
          form1.Listbox2.Items.add(Nowy^.Imie);
          form1.Listbox3.Items.Add(Nowy^.Nazwisko);
          form1.Listbox4.Items.add(Nowy^.Telefon);
          form1.ListBox4.Items.add(Nowy^.Adres);
          Nowy:= Nowy^.Nastepny;
        end;
  end;
end.

A w unit2.

unit Unit2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm2 = class(TForm)
    StaticText1: TStaticText;
    StaticText2: TStaticText;
    StaticText3: TStaticText;
    StaticText4: TStaticText;
    StaticText5: TStaticText;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Button1: TButton;
    Button2: TButton;
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

uses Unit1;

procedure TForm2.Button1Click(Sender: TObject);
begin
  AssignFile(Plik_Bazy,'Plik_do_bazy.txt');
  Reset(Plik_Bazy);
  Seek(Plik_Bazy, FileSize(Plik_Bazy));
  New(Nowy);
  SetLength(Nowy^.Lp, Length(IntToStr(FileSize(Plik_Bazy) + 1)));
  SetLength(Nowy^.imie, Length(Edit1.Text));
  SetLength(Nowy^.nazwisko, Length(Edit2.Text));
  SetLength(Nowy^.telefon, Length(Edit3.Text));
  SetLength(Nowy^.adres, Length(Edit4.Text));
  Nowy^.Lp := IntToStr(FileSize(Plik_Bazy) + 1);
  Nowy^.Imie := Edit1.Text;
  Nowy^.Nazwisko := Edit2.Text;
  Nowy^.Telefon := Edit3.Text;
  Nowy^.Nastepny := nil;
  Nowy^.Adres := Edit4.Text;

  Write(Plik_Bazy, Nowy^);
  CloseFile(Plik_Bazy);
  Edit1.Clear;
  Edit2.Clear;
  Edit3.Clear;
  Edit4.Clear;
  Close;

end;

procedure TForm2.Button2Click(Sender: TObject);
begin
Close;
end;

end. 

W unit2 dodaje recordy do pliku. W pliku pojawiają się różne dziwne znaki. Dlaczego? I teraz jak w unit1 próbuję wczytać moje dane do Listboxa to wyskakuje mi error

Project Project1.exe raised exception class EAccessViolation with message at adress 0052737b in module 'Project1.exe. Read of address 000003AC

CZemu tak się dzieje? Jak to rozwiązać? Poprawnie używam wskaźników? Jestem początkujący.

2

for i := 0 to FileSize(Plik_Bazy)-1 do

0
Patryk27 napisał(a):

for i := 0 to FileSize(Plik_Bazy)-1 do

co z tym nie tak?

że tu powinno byc tak:

 for i := 0 to FileSize(Plik_Bazy)-1 do
       begin
        New(Nowy);
        Read(Plik_Bazy, Nowy^);
        if Pierwszy = nil then
          begin
            Pierwszy:=Nowy;
            Ostatni :=Pierwszy
          end
          else begin
            Ostatni^.Nastepny := Nowy;
            Ostatni:= Nowy;
          end;
      end;

ale to i tak nie rozwiazuje tego błędu;/

2

Lista jednokierunkowa za mała pojemność. StringGrid się zawiesza przy wczytaniu dużej ilości danych.

Parę postów niżej. Pytanie zupełnie inne ale jest gotowy przykład działającej listy jednokierunkowej z możliwością zapisu/odczytu do/z pliku.

A co do dziwnych znaczków to jak próbujesz zapisać wskaźnik na następny rekord do pliku to to właśnie są te dziwne znaczki to raz. A dwa jak odczytasz zapisany wskaźnik z pliku to nie będzie on już wskazywał na następny rekord w bazie tylko na jakieś random miejsce w pamięci.

Proponuję przeczytać najpierw te dwa linki:
Wskaźniki
Wskaźniki. Listy jedno i dwukierunkowe.
Następnie je zrozumieć i dopiero potem brać się za wskaźniki.

Myślę, że następne Twoje pytania w tym temacie dotyczące budowania list są bez celu bo podałem Ci trzy linki które w 100% uzupełnią Ci wiedzę. A mianowicie: Co to są wskaźniki? Jak tworzyć listy? I gotowiec.

0

Już czytałem te artykuły. Zapisywanie pliku zmieniłem i nie używam wskaźnika.
zrobiłem :

var
db: baza;

i potem

Write(plik_bazy, db);

Czyli już nie jako wskaźnik. a co z odczytaniem?
Tylko jak odczytuje z pliku to muszę przechować to w liście jednokierunkowej. Dlaczego wyskauje mi ten Error? Jak najadę w Embarcadero na te listboxy w pliku po wywaleniu błędu to pokazuje mi te elementy z pliku. Jak można go rozwiązać?

  1. Jak bym wykorzystał bazę BDE, bo trochę czytałem. To tam można jakoś zastosować listy jednokierunkowe do czegoś?

usunięcie zbędnych pustych linii z kodu - fp

1

Ja nie mówiłem, żeby nie używać wskaźnika do zapisywania danych do pliku tylko, żeby nie zapisywać wskaźnika do pliku. Czytaj ze zrozumieniem.

0

aha to co ja przypisywałem Nastepny do nil....no tak racja, bez sensu...A co możesz zaradzić na to wypisywanie do listboxa?

0

Kod za długi i nie chce mi się analizować dokładnie. Ale widzę pewną niekonsekwencję. Mianowicie piszesz pod VCL, a używasz konstrukcji typowych jeszcze dla Turbo Pascala chyba. Dlaczego nie skorzystać z typowych udogodnień VCL, takich jak TFileStream lub choćby TMemoryStream? O wiele łatwiej to ogarnąc nimi według mnie.

Przeanalizuj sobie kod projektu, który dołaczyłem do tego posta. Powinien on Tobie pomóc ogarnąc zapis/odczyt danych do/z pliku. A i wiem, kod nie jest idealny. Ale działa. Powinieneś dzięki niemu zrozumieć ideę. Pisany dawno temu, na szybko i na bazie kodu, który kiedyś tutaj był opublikowany w downloadzie gotowców przez Adama Boducha.

0

bo to projekt, do szkoly, a ze wczesniej w tym nic nie robilem to tak na szybko patrze jakies tutoriale, zeby cokolwiek ogarnac...;/ I musza byc pewne elementy ,takie jakie sobie szkola wymyslila, wiadomo, ze szybciej zrobic baze danych jakas tam w BDE czy cos..

0

No w BDE to na pewno nie za łatwo, bo AFAIK to strasznie przestarzałe. Prędzej pewnie sprawił by się SQLite i to bez konieczności instalacji BDE oraz innych kombinacji. Niemniej jednak, skoro korzystasz z kontrukcji VCL, nie widzę problemu żebyś się wykazał i je zastosował. Chyba lepiej jeśli samodzielnie już pokażesz, że umiesz coś ponad podstawy i użycie przestarzałych konstrukcji.

Bawienie się w jakieś jak widzę wskaźniki i chyba próbę ogarnięcia listy jednokierunkowej czy czegoś w tym stylu, to imo masochizm. Usiąść, pomyśleć, następnie zakodzić to samodzielnie z głową i z użyciem elementów, które udostępnia VCL. Nikt Tobie za to głowy urwać nie powinien. Jeżeli nauczyciel niechętnie patrzy na rozwiązania ponadprogramowe, ale robiące to czego wymaga. To chyba powinien zmienić zawód albo nie wiem nauczać WF'u ;/

0

@imosek8 - od początku głównym problemem jest ta linia:

Write(Plik_Bazy, Nowy^);

która zapisuje do pliku całą strukturę spod tego wskaźnika, czyli:

  • liczbę porządkową (jeśli Lp to oznacza),
  • imię,
  • nazwisko,
  • telefon,
  • adres,
  • wskaźnik na następny element;

Z tych danych niepotrzebne w pliku są:

  • liczba porządkowa (w algorytmie odczytującym zawartość pliku w każdej iteracji ustawiasz tę liczbę automatycznie),
  • wskaźnik na kolejny element listy (jest to karygodny błąd, bo po ponownym uruchomieniu programu po ustawieniu jego adresu wskaźnik będzie wskazywać na zupełnie inne miejsce);

Co więc polecam - zostaw w spokoju pliki typowane, a zastąp je klasą TFileStream lub TMemoryStream, które są bardzo wygodne w obsłudze; Do pliku zapisuj jedynie ważne dane, które po ponownym uruchomieniu programu nie zmienią się, czyli:

  • imię,
  • nazwisko,
  • telefon,
  • adres;
    Nic więcej nie potrzebujesz (jeśli chodzi o pojedynczy węzeł listy);

Aby zapisać całą listę do pliku, trzeba:

  1. utworzyć klasę strumienia w pamięci,

  2. wpisać na początku pliku ilość wszystkich węzłów listy, które chcesz zapisać,

  3. w pętli od pierwszego do ostatniego węzła listy:

  4. wpisać imię delikwenta,

  5. wpisać jego nazwisko,

  6. wpisać jego telefon,

  7. wpisać adres,

  8. zamykasz strumień zwalniając klasę z pamięci;
    Aby teraz odczytać listę z pliku należy:

  9. utworzyć klasę strumienia w pamięci,

  10. odczytać ilość wszystkich węzłów do zmiennej,

  11. w pętli do ilości wszystich węzłów ze zmiennej:

  12. zaalokować pamięć dla nowego węzła,

  13. ustawić wskaźnik na poprzednie węzeł (jeśli istnieje),

  14. wczytać imię delikwenta do pola struktury,

  15. wczytać jego nazwisko do pola struktury,

  16. wczytać jego telefon do pola struktury,

  17. wczytać adres do pola struktury,

  18. zamknąć plik zwalniając z pamięci klasę strumienia;
    Przeczytaj sobie nieco o tych klasach i popróbuj, a na pewno się uda.

0

@olesio @furious programming

A teraz szczerze napiszcie. Czy myślicie że on ogarnie TFileStream i TMemoryStream skoro nie może ogarnąć listy jednokierunkowej wraz z gotowcem?

0

Nie wiem. Ale powinien. Klasy w połączeniu z gotowcem ode mnie oraz krokowym opisem od FP powinny pomóc. Jeżeli nie da rady, to pozostaje złożenie zlecenia w dziale Oferty pracy. Tylko co jeśli nauczyciel też tu zagląda?

0

@babubabu - a myślisz, że listy jednokierunkowe (i ogólnie wskaźniki) ogarnia? Poza tym wykorzystanie w tym przypadku plików typowanych jest błędem, bo nie ma możliwości skutecznego zapisu całej struktury tak, by po wczytaniu listy z pliku wszystko działało; Nie można przy zapisie wykluczyć jakiegoś pola, dlatego zasugerowałem pliki amorficzne i strumienie;

Sądzę, że obsługa klasy strumienia (bez względu na to, czy jest nią TFileStream czy TMemoryStream) jest prostsza od własnej implementacji listy jednokierunkowej, bo nie ma zabawy ze wskaźnikami, których większość początkujących po prostu nie rozumie i gubi się w nich; A do tego jeszcze dochodzą pliki typowane, których obsługa też jest specyficzna;

Trzeba także zwrócić uwagę na to, że pliki typowane i ich obsługa są tutaj głównym problemem - nie było by go gdyby skorzystać z jakichkolwiek innych plików (choćby tekstowych, czy amorficznych), lub ww. strumieni; Wtedy każde pole trzebaby zapisać osobno, a nie całą strukturę, dlatego pewnie była by większa szansa na zwrócenie uwagi na zapis wskaźnika;

Jeśli pytacz nadal nie będzie rozumiał o czym piszę ja, czy @olesio - zawsze można machnąć lepsze wyjaśnienie - w końcu trybiki zaskoczą i dalej pójdzie gładko :]

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