Jak zapisac a potem odczytac dynamiczna tablice?

0

Jest np. tak:

type

  TLogItem = record
    LogTime: String;
    ID: LongInt;
    Name: String;
  end;
  TLog = array of TLogItem;

var
  Tablica: TLog;

Określiłem długość (SetLength), wypełniłem rekordy itd. Teraz chciałbym to zapisać do pliku. Jak to zrobić, żeby potem móc to odczytać nie znając ani długości tej tablicy, ani długości poszczególnych elementów rekordu (typ String)?

WinXP/Delphi 7 PE

Pozdrawiam,
Jacek

0

Ja bym to wrzucil do XML'a (tak zreszta robie u siebie). Ustal sobie jakis znacznik jako separator rekordow, a w nim bedziesz mial wartosci poszczegolnych itemow np.:

<rekord> <costam>value</costam> <costam2>blahblah</costam2> </rekord>

Ogolnie sa to rzeczy, przy ktorych trzeba troche samemu powymyslac, nie ma gotowych f-cji Array.SaveToFile :).

0

http://4programmers.net/Forum/viewpost.php?id=104015&h=

Może to coś pomoże.

[dopisane]
Zapisuj długość tylko dynamicznych elementów (oczywista sprawa). Poza tym, jeżeli cos takiego uważasz za upierdliwe, to muszę cię zmartwić... Pisanie programów nie jest twoim powołaniem. Jeśli będzie więcej elementów w rekordzie, to w kodzie będzie odpowiednio więcej wywołań blockread/blockwrite (2 więcej dla kazdej dynamicznej tablicy i 1 dla statycznych danych).

0

flabra: robiłem tak, ale to nie bardzo mi to dzialało, poza tym wydaje się być bardzo upierdliwe, jeśli elementów w rekordzie będzie więcej...

Jacek

0

var
Form1: TForm1;
tablica:array of longint;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var f:TextFile;
i:integer;
begin
assignfile(f,'Plik.txt');
rewrite(f);
setlength(tablica,13);
for i:=0 to 12 do
begin
tablica[i]:=random(2000);
writeln(f,tablica[i]);
end;
closefile(f);
end;

procedure TForm1.Button2Click(Sender: TObject);
var f:TextFile;
liczba,i:integer;
begin
i:=0;
assignfile(f,'Plik.txt');
reset(f);
while not eof(f) do
begin
readln(f,liczba);
setlength(tablica,i+1);
tablica[i]:=liczba;
Memo1.Lines.Add(intToStr(tablica[i]));
end;
closefile(f);
end;

Przerób sobie - ale ze stringami mogą być problemy

0

Jest np. tak:

type

  TLogItem = record
    LogTime: String;
    ID: LongInt;
    Name: String;
  end;
  TLog = array of TLogItem;

var
  Tablica: TLog;

Po pierwsze musisz określić max długość Stringa, np: zm: string[15];

powtórzę deklarację, żeby bylo lepiej widoczne:

var
 Tablica: array of TLogItem;
 plik: file of TLogItem;

procedure zapis;
begin
 assignFile(plik, 'C:\plik.dat');
 rewrite(plik);
 write(plik, tablica);
 CloseFile(plik);
end;

procedure odczyt;
var
 i: integer;
begin
 assignFile(plik, 'C:\plik.dat');
 reset(plik);
 setLength(tablica, fileSize(plik));
 for i:=0 to fileSize(plik) do
      begin
       read(plik, tablica[i].logTime);
       //pozostałe elementy tablicy
      end;
 CloseFile(plik);
end;

Wszystko pisane "na żywo"

Dla większego bezpieczeństwa podczas odczytywania można zwiększyć rozmiar tablicy jeszcze np o 2.
Ale musisz pamiętać o tym, że zwiększałeś nadmiarowo...

0

Po pierwsze musisz określić max długość Stringa, np: zm: string[15];

Dopiszę się:

Bo długi string jest wskaźnikiem, więc zapisujesz tylko 4bajtowy wskaźnik, co nie ma sensu. Przy użyciu krótkich stringów zapisujesz zawartość.

Jeśli musisz mieć długie stringi, zapomnij o rekordach i po prostu zapisz je (tzn. ich zawartość:

BlockWrite(F, nazwastringu[1], Length(nazwastringu)) 

, gdzie F jest file

 lub <code class="delphi">file of byte

) ciurkiem oraz tablicę offsetów, albo przeplataj długość-string-długość-string itd. Pierwsza wersja daje szybszy dostęp przez Seek

od razu do konkretnego stringu, w drugiej musisz czytać wszystkie poprzednie stringi zanim dojdziesz do żądanego. Z kolei pierwsza wersja wymaga intelignetnego skonstruowania tablicy offsetów, zwłaszcza jeśli przewidujesz dopisywanie do i kasowanie z pliku. Ja w podobnej sytuacji (co prawda nie na stringach ale na innych strukturach o silnie zmiennej wielkości) trzymałem tablicę offsetów w innym pliku. To jest względnie proste do napisania, ale potencjalnie niebezpieczne: jeśli masz awarię między zapisem do tablicy a zapisem do właściwego pliku, albo jak user wytnie jeden plik, to leżysz.
Jeśli stringi są względnie podobnej wielkości to polecam użycie string[n] jako pola w rekordzie i wielokrotnie już  podawane przeze mnie rozwiązanie:
```delphi
type R=packed record
 s: string[10];
 e: longint;
{lub tp.}
end;

var
 AR: array of R;
 FR: file of R;

odczyt:

AssignFile(FR,nazwapliku);
Reset(FR);
SetLength(AR,FileSize(FR));
BlockRead(FR,AR[0],FileSize(FR));
CloseFile(FR);

Zapis

AssignFile(FR,nazwapliku);
Rewrite(FR);
BlockWrite(FR,AR[0],Length(AR));
CloseFile(FR);

Lub dostęp do poszczególnych rekordów przez Seek i Read/Write lub BlockRead/BlockWrite

0

Tylko jest taki zonk, ze stringa sie nie zapisze Write(jakisstring) bo wyjda jakies smieci. Można zapisywać po literce, ale można też tak:
(pisane z pamięci, wiec moze nie dzialac)

// zapis
var 
  F: TFileStream; 
  pText: PChar; 
  sText: string; 
  W: Word; // dlugosc stringa
begin
  sText := 'bzz'; 
  w := Length(sText); 
  pText := StrAlloc(Length(sText) + 1); 
  StrPLCopy(pText, sText, Length(sText)); 
  F.Write(w, SizeOf(w)); 
  F.Write(pText^, w); 
  StrDispose(pText); 
end;

//odczyt
var 
  F: TFileStream; 
  pText: PChar; 
  PTemp: PChar; 
  W: Word; // dlugosc stringa
begin
   F.Read(w, SizeOf(w)); 
   pText := StrAlloc(w + 1); 
   pTemp := StrAlloc(w + 1); 
   F.Read(pTemp^, W); 
   StrLCopy(pText, pTemp, W); 
   //
   StrDispose(pTemp); 
   StrDispose(pText); 
end;

0

Tylko jest taki zonk, ze stringa sie nie zapisze Write(jakisstring) bo wyjda jakies smieci. Można zapisywać po literce, ale można też tak:

Najprościej tak:

F:file of byte;
S:string;

BlockWrite(F,s[1],Length(s));

0
var 
  s:string; // nie ważne ansi, czy short, to akurat nie rzutuje.
  f:file;
  i:integer;

assign(f,'nazwa');  // assignfile(f,'nazwa');
rewrite(f,1);  // przy file of char/byte/boolean samo rewrite(f);
i:=length(s); 
blockwrite(f,i,sizeof(i)); // sizeof(i)=2/4 w zależności od kompilatora/trybu
blockwrite(f,s[1],i);
close(f);   //closefile(f)

assign(f,'nazwa');
reset(f,1);
blockread(f,i,sizeof(i));
setlength(s,i);  // dla shortstringa moze byc s[0]:=char(i);
blockread(f,s[1],i);
close(f);

Zakładając, że sizeof(i) jest niezmienne w czasie, dla tego samego kompilatora i trybu docelowego... Nie ma prostszej metody. Dla widestring'a wystarczy tylko w wywołaniech blockread/blockwrite(f,s[1],i) zmienić i na (i*2) lub (i shl 1), wynik będzie ten sam.

// Przy pierwszej odpowiedzi myslałem, że poradzisz sobie z różnicą pomiędzy dynamiczną array of sth a stringiem (indeksowanie od 0 lub od 1). Ale widać za duzo zakładałem.

// na koniec... ten nieciekawy wątek za długo sie ciągnie...

type
  TLogItem = record
    LogTime: string;
    ID: LongInt;
    name: string;
  end;
  TLog=array of TLogItem;

var
 Tablica:TLog; 

procedure save(name:string;var t:tlog);
var
  f:file;
  i,j:integer;
begin
  assign(f,name);
  rewrite(f,1);
  for i:=0 to length(t)-1 do
    with(t[i])do
      begin
        j:=length(logtime);
        blockwrite(f,j,sizeof(j));
        blockwrite(f,logtime[1],j);
        blockwrite(f,id,sizeof(id));
        j:=length(name);
        blockwrite(f,j,sizeof(j));
        blockwrite(f,name[1],j);
      end;
  close(f);
end;

procedure load(name:string;var t:tlog);
var
  f:file;
  i,j:integer;
begin
  assign(f,name);
  reset(f,1);
  i:=0;
  while(not eof(f))do
    begin
      inc(i);
      setlength(t,i);
      with(t[i-1])do
        begin
          blockread(f,j,sizeof(j));
          setlength(logtime,j);
          blockread(f,logtime[1],j);
          blockread(f,id,sizeof(id));
          blockread(f,j,sizeof(j));
          setlength(name,j);
          blockread(f,name[1],j);
        end;
    end;
  close(f);
end;

// sorki pq.. nie przeczytałem wcześniej ;p

0

Czyli cały czas zapisujecie tylko po to, żeby zapisywać. Nikt nie pomyślał o tym, żeby te dane przygotować tak, żeby je później odczytać.

Chciałbym zwrócić uwagę na mój post powyżej datowany 04-03-2004 11:29. Podaję tam dwa pomysły na konstrukcję zapisu i odczytu danych o zmiennej długości, jak stringi.

0

Dziękuje wszystkim za pomoc. W końcu poszedłem na kompromis. Tablica jest dynamiczna, ale stringi mają stała długość 255 znaków, może to nie jest oszczędność, ale działa. Ponieważ w pliku piszę nie tylko te tablice, więc skorzystałem z fHandle := FileCreate(FName), a potem FileWrite, gdzie najpierw piszę rozmiar elementu, potem całą tablicę złożoną z rekordów, przy odczycie odwrotnie - wiem, ile rekordów czytać (wielkość tablicy). Całym problemem były łańcuchy bez określonej długości, czyli de facto dynamiczne, wewnątrz dynamicznej tablicy. Rekordy nie miały więc jednakowej długości i trzebaby strasznie "drobić procedurę odczytu. Wiec dałem sobie spokój.

Dzięki pozdrawiam,
Jacek

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