FreePascal - zapis i odczyt typu rekordowego do pliku

0

Witam. Projektuje prosta bazę danych sklepu budowlanego i chciałem zapisać i odczytać ją do pliku przez zmienną plik: File. Jednak jest problem przy odczycie z zapisanego już pliku. Najważniejsze fragmenty kodu:

artykul = record
        nazwa : string[20];
        dzial : dzialy;
        jednostka : jednostki;
        ilosc : integer;
        max : integer;
        cena : double;
        dostepnosc : boolean;
        end;

var magazyn : array [1..1000] of artykul;
                    klawisz : char;
                    i,j : integer;
                    plik : File of artykul;

//------------------------------------------------------
repeat
Readln(klawisz);
if Upcase(klawisz)='S' then
        begin
           clrscr;
           assign(plik,'plik.dtb');
           rewrite(plik);
           for j:=1 to i do
           begin
               Write(plik,magazyn[i]);
           end;
           close(plik);
           Writeln('Zapisano do pliku.');
           Writeln;
           Writeln('Wcisnij dowolny klawisz...');
           readkey;
        end;
   if Upcase(klawisz)='R' then
        begin
           clrscr;
           assign(plik,'plik.dtb');
           reset(plik);
           i:=0;
           while not eof(plik) do
           begin
               i:=i+1;
               Read(plik,magazyn[i]);
           end;
           close(plik);
           Writeln('Odczytano baze z pliku. Wcisnij dowolny klawisz...');
           readkey;
        end;
until Upcase(klawisz)='Q';

Po zapisaniu do pliku, zamknięciu programu, ponownym uruchomieniu i odczytaniu pliku, wyświetla mi albo ostatni zapisany rekord w tablicy, a na samym końcu w ogóle pusty rekord. Chciałem zmienić write, na writeln jak podano w kursie na tej stronie ale FreePascal podaje, że tak nie można i nie mam totalnie pomysłu na czym polega błąd. Będę wdzięczny za każdą pomoc.

1
           for j:=1 to i do
           begin
               Write(plik,magazyn[i]);
           end;

sprawdź indeksy.

0

array [1..1000] of artykul;

Co to jest? C? Jak już masz dobry kompilator to używaj dobrych rzeczy w stylu dynamic array.

chciałem zmienić write, na writeln jak podano w kursie na tej stronie ale FreePascal podaje, że tak nie można

Bo tak nie można. To jest plik rekordowy i używa się na nim read i write.

Nie wiem jak masz skonfigurowany FPC ale twór rekord ma parę problemów.
nazwa : string[20]; -> nazwa :shortstring[20];.

dzial : dzialy;
jednostka : jednostki;

Co to jest? Typy zaczyna się od literki T, stosuj się ten konwencji i pokaż co są za typy.

Poza tym, wskaż jaką wersję FPC masz, starsze potrafią odwalać z refcountowanymi rzeczami.

0

Do prostej bazy użyj SQLite.
Do większej PostgreSQL.

Bazy typu "File of artykul" stosuje się tylko na uczelniach w programach na zaliczenie.

0
Azarien napisał(a):
           for j:=1 to i do
           begin
               Write(plik,magazyn[i]);
           end;

sprawdź indeksy.

Faktycznie tu niedopatrzenie, powinno być magazyn[j]. Z tym, że teraz odczytuje wszystko ładnie po kolei oprócz ostatniego rekordu. Mam np. zapisane 4 rekordy. Podczas odczytywania pętla wykonuje się 4 razy (bo później przy wyświetlaniu, wyświetla 4 rekordy, czyli i=4) ale ostatni rekord jest pusty;/

Edit:

vpiotr - no jakbyś zgadł :P i w zasadzie wszystko już mam, wiem co i jak, tylko ten zapis do pliku;/

-321oho - FPC 2.6.0

0
   if Upcase(klawisz)='R' then
        begin
           clrscr;
           assign(plik,'plik.dtb');
           reset(plik);
           i:=0;
           while not eof(plik) do
           begin
               i:=i+1;
               Read(plik,magazyn[i]);
           end;
           close(plik);
           Writeln('Odczytano baze z pliku. Wcisnij dowolny klawisz...');
           readkey;
        end;

WTF...?

Nie wiem jaki kurs przeglądasz, ale odczytywanie struktur z pliku typowanego znacznie lepiej oprzeć na pętli for - dokładnie tak, jak przedstawiono w kompendium na tej stronie:

AssignFile(Plik, 'plik.dtb');
try
  Reset(Plik);

  { indeksy pętli dostosuj do macierzy Magazyn }
  for I := 0 to Pred(FileSize(Plik)) do
    Read(Plik, Magazyn[I]);
finally
  CloseFile(Plik);
end;

i nie ma siły, by coś spowodowało błąd;


var magazyn : array [1..1000] of artykul;

Zlituj się... Zawsze potrzebne jest tysiąc elementów w macierzy? Tak jak wspomniał @-321oho - zainteresuj się macierzami dynamicznymi, o których oczywiście można przeczytać w kompendium; Ich obsługa jest prawie tak prosta, jak macierzy statycznych, wystarczy poznać SetLength i do dzieła;


vpiotr napisał(a)

Bazy typu "File of artykul" stosuje się tylko na uczelniach w programach na zaliczenie.

Tak...? W takim razie sensu większego nie było dodawać ich do możliwości języka; Szkoda, że ludzie myślą, że najlepiej dla paru struktur babrać się z systemami bazodanowymi, zamiast prosto i oszczędzając sobie roboty napisać dwie proste procedury - odczyt/zapis (tak proste, jak podałem wyżej w kodzie);

Nie dość, że trzeba się do tego nauczyć ich obsługi, to jeszcze dochodzi przyswajanie zapytań, które niejednemu początkującemu programiście sprawiają problemy (świadczą o tym dziesiątki tematów odnośnie "niedziałających" zapytań)...

Wybacz, ale ja kilkanaście (a może i więcej) razy wykorzystywałem z powodzeniem pliki typowane i nie miałem zamiaru tracić czasu na większe systemy bazodanowe, dla mnie prosta funkcjonalność = prosty algorytm; Przy większych programach oczywiście zaleca się skorzystanie choćby z SQLite, jednak pytaczowi na pewno wystarczą poczciwe pliki typowane, jak tylko nauczy się wykorzystywania macierzy dynamicznych;

0

Ogranicz program do minimum, które da się skompilować a które nadal będzie wykazywało błąd. Przynajmniej ktoś na forum będzie mógł program przetestować.
Zresztą nawet nie musisz go pokazywać — po okrojeniu programu często błąd staje się oczywisty.

0
furious programming napisał(a):
vpiotr napisał(a)

Bazy typu "File of artykul" stosuje się tylko na uczelniach w programach na zaliczenie.

Tak...? W takim razie sensu większego nie było dodawać ich do możliwości języka; Szkoda, że ludzie myślą, że najlepiej dla paru struktur babrać się z systemami bazodanowymi, zamiast prosto i oszczędzając sobie roboty napisać dwie proste procedury - odczyt/zapis (tak proste, jak podałem wyżej w kodzie);

Nie dość, że trzeba się do tego nauczyć ich obsługi, to jeszcze dochodzi przyswajanie zapytań, które niejednemu początkującemu programiście sprawiają problemy (świadczą o tym dziesiątki tematów odnośnie "niedziałających" zapytań)...

Wybacz, ale ja kilkanaście (a może i więcej) razy wykorzystywałem z powodzeniem pliki typowane i nie miałem zamiaru tracić czasu na większe systemy bazodanowe, dla mnie prosta funkcjonalność = prosty algorytm; Przy większych programach oczywiście zaleca się skorzystanie choćby z SQLite, jednak pytaczowi na pewno wystarczą poczciwe pliki typowane, jak tylko nauczy się wykorzystywania macierzy dynamicznych;

Ta konstrukcja powstała w latach kiedy SQL kojarzył się jedynie z lotniskiem w San Carlos a dBase to był szczyt marzeń bazodanowca.
Co nie znaczy że nadal warto / należy to stosować.
Owszem, do jednej rzeczy można: przetwarzanie batchowe.
Ale jak tylko okaże się że trzeba dodać do zbioru wyszukiwanie lub usuwanie rekordów, warto pomyśleć o bazie SQL.

0
Program sbudowlany;
uses crt;

type dzialy = (Elektryka,Hydraulika,Narzedzia,Art_Metalowe,Materialy,Inne);
type jednostki = (sztuk,metr,kg,litr);

artykul = record
        nazwa : string[20];
        dzial : dzialy;
        jednostka : jednostki;
        ilosc : integer;
        max : integer;
        cena : double;
        dostepnosc : boolean;
        end;

var magazyn : array [1..1000] of artykul;
    klawisz : char;
    i,j : integer;
    plik : File of artykul;

Procedure nowy(var magazyn : array of artykul);
        begin
          clrscr;
          writeln('Dodawanie nowego artykulu: ');
          writeln();
          write('Podaj nazwe artykulu: ');
          Readln(magazyn[i].nazwa);
          write('Cena za jednostke: ');
          Readln(magazyn[i].cena);
          writeln('--- Dodano nowy artykul do sklepu ---');
          writeln('Wcisnij dowolny klawisz...');
          readkey;
        end;

Procedure wyswietl(magazyn : array of artykul);
        begin
         clrscr;
          writeln('Wszystkie artykuly w sklepie: ');
          writeln;
          for j:=1 to i do
                begin
                  writeln(j,' --------------------------------------');
                  writeln('Nazwa    :   ',magazyn[j].nazwa);
                  writeln('Cena     :   ',magazyn[j].cena:1:2);
                end;
          writeln('B - powrot do glownego menu');
          repeat
          readln(klawisz);
          until (Upcase(klawisz)='B');
        end;

Begin
i:=0;
repeat
   clrscr;
   Writeln('N - nowy artyku, W - wyswietl wszystkie artykuly, R - wczytaj,  S - zapisz, Q - koniec');
   Readln(klawisz);
   if Upcase(klawisz)='N' then
        begin
          i:=i+1;
          nowy(magazyn);
        end;
   if Upcase(klawisz)='W' then
        begin
          wyswietl(magazyn);
        end;
   if Upcase(klawisz)='S' then
        begin
           clrscr;
           assign(plik,'plik.dtb');
           rewrite(plik);
           for j:=1 to i do
           begin
               Write(plik,magazyn[j]);
           end;
           close(plik);
           Writeln('Zapisano do pliku.');
           Writeln;
           Writeln('Wcisnij dowolny klawisz...');
           readkey;
        end;
   if Upcase(klawisz)='R' then
        begin
           clrscr;
           i:=0
           Assign(plik,'plik.dtb');
           reset(plik);
           while not eof(plik) do
           begin
               i:=i+1;
               Read(plik,magazyn[i]);
           end;
           Close(plik);
           Writeln('Odczytano baze z pliku. Wcisnij dowolny klawisz...');
           readkey;
        end;
until Upcase(klawisz)='Q';
end.
0

przecież w tym programie nie ma żadnych "refcountowanych" rzeczy.

Definicja string[20] może być refcountowana (zależy od ustawień). Tak samo nie wiadomo czym są tamte typy.

Nie wiem jaki kurs przeglądasz, ale odczytywanie struktur z pliku typowanego znacznie lepiej oprzeć na pętli for - dokładnie tak, jak przedstawiono w kompendium na tej stronie:

Mnie tam uczyli while not eof(plik) do

Podczas odczytywania pętla wykonuje się 4 razy (bo później przy wyświetlaniu, wyświetla 4 rekordy, czyli i=4) ale ostatni rekord jest pusty;/

A próbowałeś z Debuggerem?

Szkoda, że ludzie myślą, że najlepiej dla paru struktur babrać się z systemami bazodanowymi, zamiast prosto i oszczędzając sobie roboty napisać dwie proste procedury - odczyt/zapis (tak proste, jak podałem wyżej w kodzie);

Zgadzam się, aczkolwiek zazwyczaj wolę pliki tekstowe. Może i wyjdę na cieniasa ale nigdy nie użyłem SQLa nawet gdy miałem dosyć dużo elementów do przetworzenia (nigdy nie było to na tyle dużo żeby SQL był aż tak potrzebny).

Co nie znaczy że nadal warto / należy to stosować. Ale jak tylko okaże się że trzeba dodać do zbioru wyszukiwanie lub usuwanie rekordów, warto pomyśleć o bazie SQL.

Osobiście zazwyczaj nie używam żadnego SQLa po prostu wszystkie dane ładuję do jakiegoś pojemnika na którym mam wszelkie operacje. No ale gdy już mamy tyle danych że nie mieszczą się w pamięci to rzeczywiście, warto pomyśleć nad SQLem.

Potestowałem trochę ostatni kod pytacza i błąd nie wynika z zapisu i odczytu pliku, więc pytanie pytacza nawet mi sprawiło parę miłych chwil z debuggerem.
Błąd leży w przekazywaniu tablicy do procedury Procedure nowy(var magazyn : array of artykul); i ''Procedure wyswietl(magazyn : array of artykul);
'' Jest to przekazywane jako open array, dziwne że FPC się na to zgodził. Nie mniej, pytaczowi z całą pewnością nie chodziło o open array (które jest indeksowane OD 0) i z tego wynikają wszystkie dalsze bugi (kod działa póki się go nie zrestartuje [co wyczyści item nr 0]).
Pytaczowi polecam:

  • Nazywanie typów na T
  • Zamiast ogromnej tablicy statycznej zrobienie tablicy dynamicznej
  • Przekazywanie tablicy dynamicznej jako tablicę dynamiczną, statycznej jako statyczną (zrób na to oddzielny typ który zastosujesz w procedurach i deklaracji).
0
-321oho napisał(a):

Szkoda, że ludzie myślą, że najlepiej dla paru struktur babrać się z systemami bazodanowymi, zamiast prosto i oszczędzając sobie roboty napisać dwie proste procedury - odczyt/zapis (tak proste, jak podałem wyżej w kodzie);

Zgadzam się, aczkolwiek zazwyczaj wolę pliki tekstowe. Może i wyjdę na cieniasa ale nigdy nie użyłem SQLa nawet gdy miałem dosyć dużo elementów do przetworzenia (nigdy nie było to na tyle dużo żeby SQL był aż tak potrzebny).

Ostatni raz takie przetwarzanie robiłem na mainframe. To były pliki tekstowo / stałopozycyjne.
Dlatego napisałem że można.

Tylko programista powinien trochę dalej wybiegać niż pytający i powinien próbować przeanalizować jego potrzeby.
Nawet jeśli pytający aktualnie tego nie jest świadomy to robi właśnie bazę typu write-once.
Czyli dopisz rekord i przetwarzaj całość.

Tak jak napisałem - jeśli chodziło o algorytm (program na zaliczenie laborki) to takie podejście jest OK.
Jeśli o zrobienie nawet małej bazki dla wujka to jednak warto się zastanowić nad SQL.
W zasadzie to nie ma innej opcji.

Co do tego "nigdy nie używałem SQL" - to ja osobiście też dużo rzeczy nie robiłem w programowaniu, co nie znaczy że gdy jak ktoś mnie się zapyta w czym zrobić projekt plakatu to mu polecę Painta, bo go znam.

0

Definicja string[20] może być refcountowana (zależy od ustawień). Tak samo nie wiadomo czym są tamte typy.

string[20] jest zawsze równoważne shortstring[20], niezależnie od tego jaki typ stringa oznacza samo string.

0

Ok, teraz moje funkcje zapisu odczytu wyglądają tak:

   if Upcase(klawisz)='S' then
        begin
           clrscr;
           AssignFile(plik,'plik.dtb');
           try
           rewrite(plik);
           for j:=1 to i do
           begin
               Write(plik,magazyn[j]);
               writeln('zapis ',j);
           end;
           finally
           CloseFile(plik);
           end;
           Writeln('Zapisano do pliku.');
           Writeln;
           Writeln('Wcisnij dowolny klawisz...');
           readkey;
        end;
   if Upcase(klawisz)='R' then
        begin
           clrscr;
           AssignFile(plik,'plik.dtb');
           i:=0;
           try
              reset(plik);
              for j:=1 to Pred(FileSize(plik)) do
              begin
                 i:=i+1;
                 Read(plik,magazyn[j]);
                 writeln('odczyt ',j);
                 writeln('petla ',i);
              end;
           finally
              CloseFile(plik);
           end;
           Writeln('Odczytano baze z pliku. Wcisnij dowolny klawisz...');
           readkey;
        end;

Do pętli dodałem kontrolne writeln'y i przy teście dla 4 nowych wpisach, zapis wykonał się 4 razy. Zamknąłem program, po ponownym uruchomieniu odczyt wykonał się 3 razy, a do tego przy wyświetlaniu wszystkich rekordów wyświetliło 3 wpisy (no bo zmienna i dojechała tylko do 3) ale ostatni 3 rekord był pusty;/

0

Tylko programista powinien trochę dalej wybiegać niż pytający i powinien próbować przeanalizować jego potrzeby.

A to pytacz programistą nie jest?

Co do tego "nigdy nie używałem SQL" - to ja osobiście też dużo rzeczy nie robiłem w programowaniu, co nie znaczy że gdy jak ktoś mnie się zapyta w czym zrobić projekt plakatu to mu polecę Painta, bo go znam.

No właśnie widać że to raczej na zaliczenie. Gdyby podstawowym zadaniem programu byłoby przetwarzanie danych to oczywiście mówiłoby się o SQLu. Tylko że tutaj wyraźnie widać że to jest program na zaliczenie więc właściwie nie rozumiem o co ci chodzi. Oświeć mnie.

string[20] jest zawsze równoważne shortstring[20], niezależnie od tego jaki typ stringa oznacza samo string.

Sprawdziłem, rzeczywiście. Zresztą FPC 2.6.0 by informował o próbie używania refcountowanych rzeczy, mówiłem o tym bo starsze wersje miały z tym problem. Nie mniej, wtedy nie miałem wiedzy czym są te deklarowane typy.

Do pętli dodałem kontrolne writeln'y i przy teście dla 4 nowych wpisach, zapis wykonał się 4 razy. Zamknąłem program, po ponownym uruchomieniu odczyt wykonał się 3 razy, a do tego przy wyświetlaniu wszystkich rekordów wyświetliło 3 wpisy (no bo zmienna i dojechała tylko do 3) ale ostatni 3 rekord był pusty;/

Jesteś ślepy, co nie? Przecież rozwiązanie podałem w swoim poprzednim poście. Ale nawet docenić mojej pracy nie można, ignorujmy.

0
reset(plik);
for j:=1 to Pred(FileSize(plik)) do
begin
  i:=i+1;
  Read(plik,magazyn[j]);
  writeln('odczyt ',j);
  writeln('petla ',i);
end;

Co ty żeś się uparł na to

for i := 1 to Pred(FileSize(plik)) do

wstaw tam zero i będzie git.

0

@babubabu , nie.
Tak samo jako @furious programming zakładasz że błąd leży przy zapisie/odczycie a nie leży. Po pierwszym patchu (ze złym ustawianiem indeksu przy zapisie) procedura odczytu jak i zapisu była w porządku. Błąd opisałem dwa posty temu, nie ma on nic wspólnego z tym czy użyjemy for czy while.
Spróbujcie sobie się pobawić samemu tym programem, po restarcie i tak będziecie tracili elementy nawet jak na głowie staniecie, po prostu procedury wyswietl i nowy mają błąd logiczny. Następnym razem proponuję testować zamiast traktować każdy problem szablonowo.

1
-321oho napisał(a)

@babubabu , nie.
Tak samo jako @Furious Programming zakładasz że błąd leży przy zapisie/odczycie a nie leży.

Założyłem tak, ponieważ napisałem sobie prostą aplikację i podany przeze mnie sposób działa bez problemu; Sprawdzałem pod FPC 2.6.0 i nie ma żadnych problemów ani z zapisem, ani z odczytem rekordów z pliku; Tak jak napisałem wcześniej - skorzystałem z pętli for i FileSize, odpowiednio ustawiając licznik pętli by zgadzał się z macierzą; W odróżnieniu od autora wykorzystałem spakowany rekord i łańcuch String[20] i problemu nie ma; Wykorzystałem te same typy co pytacz, napisałem także pomocnicze procedurki do wypełnienia (FillMagazine) oraz wyświetlenia macierzy (PrintMagazine) - całość poniżej:

program MyLibrary;
uses
  Crt;

type
  TSection = (secElectricity,
              secHydraulics,
              secTools,
              secMetalArticles,
              secMaterials,
              secOther);

type
  TEntity = (entPieces,
             entMeter,
             entKilogram,
             entLiter);

type
  TArticle = packed record
    FName: String[20];
    FSection: TSection;
    FEntity: TEntity;
    FCount: Integer;
    FMax: Integer;
    FPrice: Double;
    FAvailability: Boolean;
  end;

type
  TMagazine = array [1 .. 1000] of TArticle;

  procedure ShowMenu();
  begin
    ClrScr;

    WriteLn('"L" - Load database');
    WriteLn('"S" - Save database');
    WriteLn('"P" - Print database'#10);
    WriteLn('"Q" - Quit'#10#10);
    Write('Select option: ');
  end;

  procedure FillMagazine(var AMagazine: TMagazine);
  var
    I: Word;
  begin
    for I := Low(AMagazine) to High(AMagazine) do
      with AMagazine[I] do
      begin
        FName := 'Foo Name';
        FSection := secTools;
        FEntity := entPieces;
        FCount := I;
        FMax := I;
        FPrice := 3.14;
        FAvailability := True;
      end;
  end;

  procedure LoadMagazine(var AMagazine: TMagazine);
  var
    fInput: File of TArticle;
    I: SmallInt;
  begin
    ClrScr;
    AssignFile(fInput, 'C:\Foo.dtb');

    try
      Reset(fInput);
      WriteLn('Loading database...');
      WriteLn('Count: ', FileSize(fInput), #10#10);

      for I := 1 to FileSize(fInput) do
        Read(fInput, AMagazine[I]);
    finally
      CloseFile(fInput);
    end;

    Write('Loading complete... Press ENTER: ');
    ReadLn;
  end;

  procedure SaveMagazine(const AMagazine: TMagazine);
  var
    fOutput: File of TArticle;
    I: SmallInt;
  begin
    ClrScr;
    AssignFile(fOutput, 'C:\Foo.dtb');

    try
      ReWrite(fOutput);
      WriteLn('Saving database...');
      WriteLn('Count: ', Length(AMagazine));

      for I := Low(AMagazine) to High(AMagazine) do
        Write(fOutput, AMagazine[I]);
    finally
      CloseFile(fOutput);
    end;

    Write('Saving complete... Press ENTER: ');
    ReadLn;
  end;

  procedure PrintMagazine(const AMagazine: TMagazine);
  var
    I: Word;
  begin
    ClrScr;

    for I := 1 to 1000 do
    begin
      with AMagazine[I] do
        WriteLn('"Index: ', I, '" ',
                '"', FName, '" ',
                '"', FSection, '" ',
                '"', FEntity, '" ',
                '"', FCount, '" ',
                '"', FMax, '" ',
                '"', FPrice:1:2, '" ',
                '"', FAvailability, '"');

      if I mod 23 = 0 then
      begin
        ReadLn;
        ClrScr;
      end;
    end;

    Write(#10#10'Printing complete... Press ENTER: ');
    ReadLn;
  end;

const
  KEY_L = 76;
  KEY_S = 83;
  KEY_P = 80;
  KEY_Q = 81;
var
  Magazine: TMagazine;
  iOption: Byte;
begin
  //FillMagazine(Magazine);

  repeat
    ShowMenu();
    iOption := Ord(UpCase(ReadKey));

    case iOption of
      KEY_L: LoadMagazine(Magazine);
      KEY_S: SaveMagazine(Magazine);
      KEY_P: PrintMagazine(Magazine);
    end;
  until iOption = KEY_Q;
end.

@maniek099 - przeanalizuj sobie kod i sprawdź gdzie Twój kod się wywala - użyj debugera do pomocy;

0

@maniek099 - przeanalizuj sobie kod i sprawdź gdzie Twój kod się wywala - użyj debugera do pomocy;

Serio, ile razy można się powtarzać że błąd leży w procedurze wyświetlającej i dodającej elementy? Co do użycia debuggera, to mam pewne wątpliwości czy ty byś temu podołał bo problem był bardzo paskudny i z całą pewnością pytacz by mu nie podołał (sam musiałem paręnaście minut siedzieć nad tym).
No ale gdzie ja, ty jesteś nieprzemakalny, wciąż mówisz jak to ładnie użyłeś czegoś tam kiedy właściwie miejscami napisałeś gorzej to co pytacz (while not eof(f) do jest lepsze niż jakieś for i praktycznie każdy kurs wskazuje taką konstrukcję) i wciąż wprowadzasz go w błąd mówiąc ni z gruszki ni z pietruszki o plikach rekordowych które tutaj nie mają znaczenia (natomiast mają arraye statyczne, dynamiczne i open).

Założyłem tak, ponieważ napisałem sobie prostą aplikację i podany przeze mnie sposób działa bez problemu; Sprawdzałem pod FPC 2.6.0 i nie ma żadnych problemów ani z zapisem, ani z odczytem rekordów z pliku; Tak jak napisałem wcześniej - skorzystałem z pętli for i FileSize, odpowiednio ustawiając licznik pętli by zgadzał się z macierzą; W odróżnieniu od autora wykorzystałem spakowany rekord i łańcuch String[20] i problemu nie ma; Wykorzystałem te same typy co pytacz, napisałem także pomocnicze procedurki do wypełnienia (FillMagazine) oraz wyświetlenia macierzy (PrintMagazine) - całość poniżej:

Zrobiłeś zupełnie nowy kod w którym inaczej niż pytacz przekazujesz parametry do procedur. Nie ty jeden umiesz napisać magazyn, ale wszystko wskazuje na to że tylko ja tutaj rozumiem jak naprawić kod pytacza.
Wiesz co? Pokaż mi pytacza który naprawisz modyfikując procedury zapisu i odczytu. Ciekaw jestem czy ci się uda, a jeżeli ci się uda to sam zobaczysz że błąd nie leży na dobrą sprawę w procedurze zapisu i odczytu tylko w reszcie rzeczy.

1
-321oho napisał(a)

Serio, ile razy można się powtarzać że błąd leży w procedurze wyświetlającej i dodającej elementy? Co do użycia debuggera, to mam pewne wątpliwości czy ty byś temu podołał bo problem był bardzo paskudny i z całą pewnością pytacz by mu nie podołał (sam musiałem paręnaście minut siedzieć nad tym).

Ależ najmniejszego problemu nie miałem, żeby znaleźć błąd, szczególnie z tak wspaniałym narzędziem jakim jest debuger; Dodałem sobie do Watches zmienne I, J, a także trzy pierwsze elementy macierzy Magazyn: Magazyn[1], Magazyn[2] i Magazyn[3]; Dzięki temu od razu zauważyłem, że dodając pierwszy artykuł do macierzy nie ląduje wcale w Magazyn[1], tylko w Magazyn[2] właśnie przez tą konstrukcję: var magazyn : array of artykul); Wystarczyło stworzyć sobie typ, np.: TMagayn = array [1 .. 1000] of Artykul; i ustawić argument w tej procedurze nowego typu, ale oczywiście kto by posłuchał, żeby indeksować macierze od 0 - lepiej nic o tym nie pisać; Poza tym denerwowały mnie deklaracje typów nie zaczynających się prefiksem T, więc je zmieniłem; Kod nieznacznie zmodyfikowany poniżej:

program Project1;
uses
  Crt;

type
  TDzialy = (Elektryka, Hydraulika, Narzedzia, Art_metalowe, Materialy, Inne);

type
  TJednostki = (Sztuk, Metr, Kg, Litr);

type
  TArtykul = packed record
    Nazwa: String[20];
    Dzial: TDzialy;
    Jednostka: TJednostki;
    Ilosc: Integer;
    Max: Integer;
    Cena: Double;
    Dostepnosc: Boolean;
  end;

type
  TMagazyn = array [1 .. 1000] of TArtykul;

var
  Magazyn: TMagazyn;
  Klawisz: Char;
  I, J: Integer;
  Plik: File of TArtykul;

  procedure Nowy(var AMagazyn: TMagazyn);
  begin
    ClrScr;
    WriteLn('Dodawanie nowego artykulu: ');
    WriteLn;
    Write('Podaj nazwe artykulu: ');
    ReadLn(AMagazyn[I].Nazwa);
    Write('Cena za jednostke: ');
    ReadLn(AMagazyn[I].Cena);
    WriteLn('--- Dodano nowy artykul do sklepu ---');
    WriteLn('Wcisnij dowolny klawisz...');
    ReadKey;
  end;

  procedure Wyswietl(AMagazyn: TMagazyn);
  begin
    ClrScr;
    WriteLn('Wszystkie artykuly w sklepie: ');
    WriteLn;

    for J := 1 to I do
    begin
      WriteLn('-------------------------------------');
      WriteLn('Nazwa: ', AMagazyn[J].Nazwa);
      WriteLn('Cena:  ', AMagazyn[J].Cena:2:2);
    end;

    WriteLn('B - powrot do glownego menu');

    repeat
      ReadLn(Klawisz);
    until (Upcase(Klawisz) = 'B');
  end;

begin
  I := 0;

  repeat
    ClrScr;
    WriteLn('N - Nowy artykul');
    WriteLn('W - Wyswietl wszystkie artykuly');
    WriteLn('R - Wczytaj');
    WriteLn('S - Zapisz');
    WriteLn('Q - Koniec');

    ReadLn(Klawisz);

    if UpCase(Klawisz) = 'N' then
    begin
      Inc(I);
      Nowy(Magazyn);
    end;

    if UpCase(Klawisz) = 'W' then
      Wyswietl(Magazyn);

    if UpCase(Klawisz) = 'S' then
    begin
      ClrScr;
      AssignFile(Plik, 'C:\Plik.dtb');

      try
        ReWrite(Plik);

        for J := 1 to I do
          Write(Plik, Magazyn[J]);
      finally
        CloseFile(Plik);
      end;

      WriteLn('Zapisano do pliku.');
      WriteLn;
      WriteLn('Wcisnij dowolny klawisz...');
      ReadKey;
    end;

    if UpCase(Klawisz) = 'R' then
    begin
      ClrScr;
      AssignFile(Plik, 'C:\Plik.dtb');

      try
        Reset(Plik);
        I := FileSize(Plik);

        for J := 1 to I do
          Read(Plik, Magazyn[J]);
      finally
        CloseFile(Plik);
      end;

      WriteLn('Odczytano baze z pliku. Wcisnij dowolny klawisz...');
      ReadKey;
    end;
  until UpCase(Klawisz) = 'Q';
end.

I całość działa bez zarzutu, sprawdzałem na wiele sposobów i nie znalazłem błędu; Odnalezienie błędu w kodzie pytacza zajeło mi ~5 minut właśnie dzięki debugerowi;


-321oho napisał(a)

No ale gdzie ja, ty jesteś nieprzemakalny

No oczywiście ja jestem zły, bo przedstawiłem ludzki sposób podziału programu na procedury, bo zmodyfikowałem identyfikatory typów, zmiennych i procedur, bo przeinaczyłem zmienne globalne na lokalne tylko dla potrzeb konkretnych procedur, bo w ogóle miałem czelność przedstawić kod, który sam bym wykorzystał i który wiem, że spełni założenia; Jestem ciekam co by było gdybym jeszcze zaimplementował macierze dynamiczne - wtedy to już w ogóle byłbym śmieciem, bo miałem czelność przedstawić coś, czego Ty nie podałeś; Dziwię Ci się, że zamiast opierdolić pytacza za stosowanie złych praktych Ty opiepszasz mnie za to, że udzieliłem kilku wskazówek co do budowy programu, formatowania, nazewnictwa itd.

-321oho napisał(a)

wciąż mówisz jak to ładnie użyłeś czegoś tam kiedy właściwie miejscami napisałeś gorzej to co pytacz (while not eof(f) do jest lepsze niż jakieś for i praktycznie każdy kurs wskazuje taką konstrukcję)

Napisz mi w czym pętla for jest gorsza od while - według mnie to nie ma różnicy, jednak while nigdy do takich zastosowań nie zaprzęgałem, więc nie mam 100% pewności co do jej zachowania; Być może masz rację, bo przecież ja nie potrafię programować i nie mam 20-letniego stażu w poważnej firmie, stąd każdy mój algorytm jest albo błędny, albo w ogóle nie optymalny; Co do kursu - ja uczyłem się z książki Adama Boducha oraz z tego artykułu, ale widać autor książki też nic na ten temat nie wie;

-321oho napisał(a)

Zrobiłeś zupełnie nowy kod w którym inaczej niż pytacz przekazujesz parametry do procedur.

Dokładnie po to, żeby pokazać pytaczowi, że istnieją inne sposoby na wykonanie róznych elementów tego programu, jak właśnie wymienione przekazywanie argumentu do tych procedur; W tym algorytmie nie zawarłem wszystkiego, co jest w programie pytacza, bo to nie gotowiec; Poza tym dodać pozostałość to kwestia kilku minut;

-321oho napisał(a)

Wiesz co? Pokaż mi pytacza który naprawisz modyfikując procedury zapisu i odczytu. Ciekaw jestem czy ci się uda, a jeżeli ci się uda to sam zobaczysz że błąd nie leży na dobrą sprawę w procedurze zapisu i odczytu tylko w reszcie rzeczy.

Aby do tego dojść wystarczył debuger i chwilka na postawienie breakpoint'ów i dodanie kilku zmiennych do okna Watches - tyle wystarczyło żeby móc odpowiedzieć na pytanie autora wątku; Ja najpierw zasugerowałem błąd właśnie w procedurze odczytu i pomyliłem się, bo niedokładnie przeanalizowałem kod; Następnie jak tylko wróciłem do komputera skopiowałem kod do edytora, przetestowałem, zobaczyłem że jest bug, podpiąłem debuger i go moment znalazłem; Reszta informacji które podałem przysłużą się do poprawienia algorytmu;

I nic do tego nie ma deklaracja łąńcucha w strukturze, bo nie ma znaczenia na pracę algorytmu czy to jest String[20], czy ShortString[2], i tak będzie działać po przeinaczeniu argumentów;


A tak nawiasem:

-321oho napisał(a)
maniek099 napisał(a)

Podczas odczytywania pętla wykonuje się 4 razy (bo później przy wyświetlaniu, wyświetla 4 rekordy, czyli i=4) ale ostatni rekord jest pusty;/

A próbowałeś z Debuggerem?

-321oho napisał(a)

Co do użycia debuggera, to mam pewne wątpliwości czy ty byś temu podołał bo problem był bardzo paskudny i z całą pewnością pytacz by mu nie podołał

Skoro wątpisz, że ten problem jest do znalezienia używając debugera (a bardzo łatwo jest go dzięki temu narzędziu znaleźć), to po co każesz mu go używać? Sam sobie przeczysz;

0

No oczywiście ja jestem zły, bo przedstawiłem ludzki sposób podziału programu na procedury, bo zmodyfikowałem identyfikatory typów, zmiennych i procedur, bo przeinaczyłem zmienne globalne na lokalne tylko dla potrzeb konkretnych procedur, bo w ogóle miałem czelność przedstawić kod, który sam bym wykorzystał i który wiem, że spełni założenia; Jestem ciekam co by było gdybym jeszcze zaimplementował macierze dynamiczne - wtedy to już w ogóle byłbym śmieciem, bo miałem czelność przedstawić coś, czego Ty nie podałeś; Dziwię Ci się, że zamiast opierdolić pytacza za stosowanie złych praktych Ty opiepszasz mnie za to, że udzieliłem kilku wskazówek co do budowy programu, formatowania, nazewnictwa itd.

Twój kod jest na tyle zmodyfikowany (jeżeli w ogóle jest zmodyfikowany) że można powiedzieć że był pisany od początku. (Ofc chodzi o poprzedni kod)
Gdyby twój kod przypominał kod pytacza to bym się nie czepiał, problem leży w tym że ani nie stwierdziłeś w czym leżał bug pytacza ani twój kod nie przypominał jego kodu na tyle żeby stwierdzić że nie pisałeś go od nowa.

Napisz mi w czym pętla for jest gorsza od while - według mnie to nie ma różnicy, jednak while nigdy do takich zastosowań nie zaprzęgałem, więc nie mam 100% pewności co do jej zachowania; Być może masz rację, bo przecież ja nie potrafię programować i nie mam 20-letniego stażu w poważnej firmie, stąd każdy mój algorytm jest albo błędny, albo w ogóle nie optymalny; Co do kursu - ja uczyłem się z książki Adama Boducha oraz z tego artykułu, ale widać autor książki też nic na ten temat nie wie;

Typowym rozwiązaniem jest użycie while not eof(x) do które jest bardziej oczywiste niż for i:=0 to filesize(x)-1 do. Przynajmniej dla mnie. Ale oba są dla kompilatora poprawne i robią to samo. Więc zupełnie bez sensu kazałeś to zmienić. Już o udziwnianiu poprzez pred nie mówiąc.

Aby do tego dojść wystarczył debuger i chwilka na postawienie breakpoint'ów i dodanie kilku zmiennych do okna Watches - tyle wystarczyło żeby móc odpowiedzieć na pytanie autora wątku; Ja najpierw zasugerowałem błąd właśnie w procedurze odczytu i pomyliłem się, bo niedokładnie przeanalizowałem kod; Następnie jak tylko wróciłem do komputera skopiowałem kod do edytora, przetestowałem, zobaczyłem że jest bug, podpiąłem debuger i go moment znalazłem; Reszta informacji które podałem przysłużą się do poprawienia algorytmu;

Nigdzie nie wspomniałeś gdzie leżał błąd dlatego nie wiedziałem że jesteś jego świadom.

I nic do tego nie ma deklaracja łąńcucha w strukturze, bo nie ma znaczenia na pracę algorytmu czy to jest String[20], czy ShortString[2], i tak będzie działać po przeinaczeniu argumentów;

Gorzej jak dasz ansistring, czyż nie? Czy ja gdzieś mówiłem że jest problem z stringiem poza tym że być może FPC go refcountuje?

Skoro wątpisz, że ten problem jest do znalezienia używając debugera (a bardzo łatwo jest go dzięki temu narzędziu znaleźć), to po co każesz mu go używać? Sam sobie przeczysz;

Początkowo również przewidywałem że błąd jest przy odczycie/zapisie (było to jeszcze zanim doszedłem do całego kodu pytacza). I nie można tego błędu tak łatwo znaleźć m.in. poprzez takie samo nazwanie tablicy wewnętrznej i zewnętrznej, indeksowaniu od 1, potem od 0 itp. Nie przeczę sobie, to się nazywa zmiana zdania pod wpływem zdarzeń.

0

-321oho Przepraszam za niedopatrzenie, przeczytałem wszystkie posty od góry do dołu i nadal coś nie trybi. Wróciłem do starego zapisu i odczytu z pliku (z while not end(eof)) i zmieniłem

magazyn:array [1..1000] of artykul

na magazyn:array of artykul

I z tego co rozumiem, teraz mam odpowiednią tablicę, którą przekazuję do moich procedur. Teraz na samym początku programu zmienna i musi być = 1 i w przypadku procedury dodawania nowego artykułu muszę ustawiać nową długość 
```delphi
setlength(magazyn,i)

i potem reszta tak samo. Tylko dlaczego pokazuje mi miszmasz typów przy ustawianiu tej długości?

0
-321oho napisał(a)

Twój kod jest na tyle zmodyfikowany (jeżeli w ogóle jest zmodyfikowany) że można powiedzieć że był pisany od początku. (Ofc chodzi o poprzedni kod)

Oczywiście, że napisałem go od nowa żeby sprawdzić czy jest problem z łańcuchami (i wspomnianym przez Ciebie refcountowaniu); Napisałem także dlaczego ten kod nie przypomina tego autora - szkoda że dopiero później - mój błąd;

-321oho napisał(a)

Gdyby twój kod przypominał kod pytacza to bym się nie czepiał, problem leży w tym że ani nie stwierdziłeś w czym leżał bug pytacza ani twój kod nie przypominał jego kodu na tyle żeby stwierdzić że nie pisałeś go od nowa.

Co w tym złego, ze napisałem kod od nowa? Sam od dawna opierniczasz mnie, żebym nie dawał gotowców i zmuszał pytacza do myślenia, więc to zrobiłem; Pokazałem sposób dzięki któremu macierz jest co prawda globalna, ale zmienne plikowe już lokalne; Także pokazałem jak przekazać macierz jako argument, co było rozwiązaniem problemu; Nie byłem pewny co do tego, czy to jedyny problem, ale jak tylko znalazłem chwilkę czasu (bo mam swoje obowiązki) to przeanalizowałem kod linijka po linijce, oczywiście nie kopiując dosłownie z forum tylko przepisując linijka po linijce od razu ucząc się go, zapamiętując co gdzie jest i formatując go tak, bym się w nim orientował; Dzięki temu problem odnalazł się w ciągu kilku chwil;

-321oho napisał(a)

Typowym rozwiązaniem jest użycie while not eof(x) do które jest bardziej oczywiste niż for i:=0 to filesize(x)-1 do. Przynajmniej dla mnie.

Dla mnie obie pętle są oczywiste i dość intuicyjne (za sprawą EoF lub FileSize), jednak nauczyłem się wykorzystywać pętlę for w miejscach, gdzie znam ilość iteracji - tutaj ich ilość jest znana, więc mogę na sztywno wykorzystać właśnie tą pętlę; Dziwi mnie natomiast fakt, że tak strasznie negowany jestem za własne rozwiązania, które różnią się od tych wyuczonych - ja rozumiem, że dany problem można rozwiązać na wiele sposobów, ale nie wszyscy to rozumieją; Dla mnie ważne jest by algorytm działał stabilnie, szybko i bezbłędnie, a czy wykorzystam do tego pętlę for, czy while, czy repeat to nie ma większego znaczenia jeśli potrafię odnaleźć się w kodzie i go zrozumieć;

-321oho napisał(a)

Nigdzie nie wspomniałeś gdzie leżał błąd dlatego nie wiedziałem że jesteś jego świadom.

Wystarczyło przeanalizować poprzedni kod, jaki podałem i znalazłbyś rozwiązanie; Ja nie byłem pewny, że to jedyny problem, więc wolałem napisać swój prosty algorytm niż babrać się z globalnymi licznikami i innym globalnym pieroństwem, którego nie nawidzę;

-321oho napisał(a)

Czy ja gdzieś mówiłem że jest problem z stringiem poza tym że być może FPC go refcountuje?

Nie, nigdzie tego nie napisałeś, jednak dałeś mi do myślenia i wolałem to przetestować;

-321oho napisał(a)

Już o udziwnianiu poprzez pred nie mówiąc.

No wybacz, ale ja zawsze w swoich kodach indeksuje macierze od 0, a że często czytam artykuły na delphi.about.com i polubiłem styl Zarko Gajic'a to zamiast -1 czy +1 wolę stosować Succ i Pred, choć służą do tego samego; Gdyby pytacz przeczytał do czego służy funkcja Pred to by wiedział, że w jego macierzy indeksowanej od 1 ta funkcja jest zbędna/nie można jej użyć;

-321oho napisał(a)

Początkowo również przewidywałem że błąd jest przy odczycie/zapisie (było to jeszcze zanim doszedłem do całego kodu pytacza). I nie można tego błędu tak łatwo znaleźć m.in. poprzez takie samo nazwanie tablicy wewnętrznej i zewnętrznej, indeksowaniu od 1, potem od 0 itp.

Błąd bardzo łatwo można było znaleźć właśnie przez postawienie breakpoint'ów na początku każdej procedury i podglądaniu debugerem zawartości macierzy; Ja nie miałem z tym najmniejszego problemu, dziwię się, że Ty - doświadczony programista - nie widzisz pomocy w debugerze w konkretnie tym przypadku;

0

Ja nie miałem z tym najmniejszego problemu, dziwię się, że Ty - doświadczony programista - nie widzisz pomocy w debugerze w konkretnie tym przypadku;

Widzę w debuggerze pomoc, oczywiście. Natomiast albo przez to że nie spodziewałem się poważnego błędu albo z jakiegoś innego powodu potrzebowałem 'aż' 10-15 minut.

No wybacz, ale ja zawsze w swoich kodach indeksuje macierze od 0, a że często czytam artykuły na delphi.about.com i polubiłem styl Zarko Gajic'a to zamiast -1 czy +1 wolę stosować Succ i Pred

Nigdy się z takim czymś nie spotkałem, ale tak, również indeksuje od 0.

Dla mnie obie pętle są oczywiste i dość intuicyjne (za sprawą EoF lub FileSize), jednak nauczyłem się wykorzystywać pętlę for w miejscach, gdzie znam ilość iteracji - tutaj ich ilość jest znana, więc mogę na sztywno wykorzystać właśnie tą pętlę; Dziwi mnie natomiast fakt, że tak strasznie negowany jestem za własne rozwiązania, które różnią się od tych wyuczonych - ja rozumiem, że dany problem można rozwiązać na wiele sposobów, ale nie wszyscy to rozumieją; Dla mnie ważne jest by algorytm działał stabilnie, szybko i bezbłędnie, a czy wykorzystam do tego pętlę for, czy while, czy repeat to nie ma większego znaczenia jeśli potrafię odnaleźć się w kodzie i go zrozumieć;

Dla mnie zapis z while był bardziej czytelny niż ten z forem, dlatego się trochę przyczepiłem że nie rozumiem po co chcesz go zmieniać.

Co w tym złego, ze napisałem kod od nowa?

Niby nic, ale wyszło z tego że nie wiesz na czym polega problem, piszesz swój kod, patrzysz że działa więc mówisz pytaczowi żeby sobie radził.

maniek099 napisał(a)

Tylko dlaczego pokazuje mi miszmasz typów przy ustawianiu tej długości?

Nie widzę żadnego oczywistego błędu. No chyba że dalej źle przekazujesz parametry do procedury (jako open array zamiast dynamic array). Musisz mieć globalny typ magazynu i go pobierać w procedurze, inaczej deklarujesz open array a nie dynamic array.

Teraz na samym początku programu zmienna i musi być = 1 i w przypadku procedury dodawania nowego artykułu muszę ustawiać nową długość

Możesz też na początku ustawić długość raz, na ilość elementów w pliku. I zamiast zmiennej i do pobierania ilości elementów używaj length(magazyn). Jest jeszcze high(magazyn) który zwróci ostatni dostępny element.

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