Pliki typowane przesunięcie wpisu góra - dół

0

Jeśli mam załóżmy plik typowany o strukturze

  THKey = packed record
    THid: Integer;
    Msg: Cardinal;//
    HKey: Longint;
    Unused: Longint;
    Result: Longint;
  end; 

A plik zawiera

#1+++++
#2+++++
#3+++++
#4+++++
#5+++++
#6+++++

jak przesunąć rekordy w pliku do góry i do dołu?
Np żeby po kliknięciu w klawisz Up wybrany element np 2 zmienił pozycje o jeden w górę w tym przypadku zamienił się miejscami z #3

#1+++++
#3+++++
#2+++++
#4+++++
#5+++++
#6+++++
0

Chcesz je przesunąć w pliku, czy w pamięci?

0

W pliku ale tak by nie wykorzystywać np label w które wpisuje odczytuje itp :)
czyli chcę uzyskać efekt mam 2 butony jeden up drugi down
Po wciśnięciu up otwieram plik przesuwam wybrany element np z id 1 na pozycje wyżej a ten z id2 niżej :) zamykam plik z zapisaną zmianą

0

Nie rozumiesz – można zmodyfikować dane w pliku, bez ładowania całej jego zawartości do pamięci, a można też całą zawartość wczytać do pamięci, zmienić co trzeba i zapisać dane z pamięci do pliku. Jeśli plik z danymi jest bardzo duży to ładowanie jego pełnej zawartości do pamięci nie jest dobrym rozwiązaniem.

Pliki typowane charakteryzują się tym, że zawierają w sobie rekordy o takim samym rozmiarze. Tak więc wystarczy otworzyć plik z prawami odczytu i zapisu, a następnie wykonać standardowy swap na odpowiednich rekordach. Przynajmniej podany przypadek da się załatwić swapem, bo przesunięcie rekordu z końca pliku na początek wymaga przesunięcia całej wcześniejszej zawartości w kierunku końca pliku.

Przy czym jeśli chodzi o obsługę pliku to zapomnij o standardowych procedurach i użyj TFileStream.

0

A możesz podać przykład takiego swapa w dół i górę ? już obojętnie czy w pamięci czy w pliku - w pliku po prostu wydaje mi się to lepsze rozwiązanie skoro plik może mieć kilkanaście mb czasem a nie mogę przewidzieć jego wielkości, a nie chce by program wykorzystywał dużo pamięci.

1

Swap to swap – nie ma w górę czy w dół, bo to zamiana elementów miejscami (nie myl z przesuwaniem elementu).

uses
  Classes;

type
  TRecord = packed record
    Index: Integer;
    Letter: Char;
  end;

type
  TRecordFile = file of TRecord;

const
  REC_SIZE = SizeOf(TRecord);

  procedure FileSwapRecords(const AFileName: String; AIndex1, AIndex2: Int64);
  var
    LStream: TFileStream;
    LRecord1, LRecord2: TRecord;
  begin
    LRecord1 := Default(TRecord);
    LRecord2 := Default(TRecord);

    LStream := TFileStream.Create(AFileName, fmOpenReadWrite);
    try
      LStream.Position := AIndex1 * REC_SIZE;  LStream.Read(LRecord1, REC_SIZE);
      LStream.Position := AIndex2 * REC_SIZE;  LStream.Read(LRecord2, REC_SIZE);
      LStream.Position := AIndex1 * REC_SIZE;  LStream.Write(LRecord2, REC_SIZE);
      LStream.Position := AIndex2 * REC_SIZE;  LStream.Write(LRecord1, REC_SIZE);
    finally
      LStream.Free();
    end;
  end;

Jak widzisz nic trudnego. Potrzebne są dwie zmienne, aby do nich wczytać dane z dwóch rekordów. Po odczycie, dane z tych pomocniczych zmiennych zapisywane są z powrotem w strumieniu, tyle że w innych miejscach. Rozmiar pliku nie ma znaczenia – tego algorytmu można użyć zarówno dla malutkich plików, jak i dla gigantycznych, bo ładuje do pamięci tylko dwie paczki danych, a nie cały plik.

To tylko przykład – dla pokazania prostoty operacji zamiany elementów miejscami. Aby bezpieczne było jego użycie, należy go zabezpieczyć przed wykroczeniem poza rozmiar pliku (czyli dodać walidację indeksów elementów). No i jak pliku nie będzie na dysku, to też poleci wyjątek.

Ale przystosowanie kodu do Twoich typów danych i dodanie zabezpieczeń pozostawiam Tobie. ;)

0

tutaj to już sobie poradzę :) ważne że jest ładny przejrzysty przykład!

0

Nie wiem w czym dokładnie piszesz ten program. W jednym wątku wrzucasz do tagów delphi i lazarus, a tutaj tylko delphi. Do tego nie wiadomo w jakiej wersji masz środowisko/kompilator.

To co podałem to kod odpowiedni dla bieżącej wersji Lazarusa – pod Delphi raczej nie skompiluje się.

0

Bo niestety tam z tym obszarem pikseli potrzebuje kodu na tą i tą platformę :) a tutaj tylko na Delphi a dokładnie XE7

1

Najlepiej by było, gdybyś napisał sobie zestaw procedur/funkcji do obsługi swoich typowanych plików. Głównie po to, aby mieć jeden konkretny sposób obsługi (czyli użycie strumieni) i jeden, zunifikowany zestaw instrukcji. Dla przykładu, wydzielić można odczyt i zapis:

procedure FileReadRecord(AStream: TStream; out ARecord: TRecord); overload;
procedure FileReadRecord(AStream: TStream; out ARecord: TRecord; AIndex: Int64); overload;

procedure FileWriteRecord(AStream: TStream; const ARecord: TRecord); overload;
procedure FileWriteRecord(AStream: TStream; const ARecord: TRecord; AIndex: Int64); overload;

{...}

procedure FileReadRecord(AStream: TStream; out ARecord: TRecord);
begin
  ARecord := Default(TRecord);
  AStream.Read(ARecord, SizeOf(ARecord));
end;

procedure FileReadRecord(AStream: TStream; out ARecord: TRecord; AIndex: Int64);
begin
  ARecord := Default(TRecord);

  AStream.Position := AIndex * SizeOf(ARecord);
  AStream.Read(ARecord, SizeOf(ARecord));
end;

procedure FileWriteRecord(AStream: TStream; const ARecord: TRecord);
begin
  AStream.Write(ARecord, SizeOf(ARecord));
end;

procedure FileWriteRecord(AStream: TStream; const ARecord: TRecord; AIndex: Int64);
begin
  AStream.Position := AIndex * SizeOf(ARecord);
  AStream.Write(ARecord, SizeOf(ARecord));
end;

I teraz tych uniwersalnych funkcji możesz użyć np. do odczytu całego pliku (wersje dwuparametrowe), ale też do swapa rekordów (wersje z trzema parametrami). Wtedy też procedurka swapująca zmieni wygląd na poniższy:

procedure FileSwapRecords(AStream: TStream; AIndex1, AIndex2: Int64);
var
  LRecord1, LRecord2: TRecord;
begin
  LRecord1 := Default(TRecord);
  LRecord2 := Default(TRecord);

  FileReadRecord(AStream, LRecord1, AIndex1);
  FileReadRecord(AStream, LRecord2, AIndex2);

  FileWriteRecord(AStream, LRecord1, AIndex2);
  FileWriteRecord(AStream, LRecord2, AIndex1);
end;

A jeszcze lepiej by było napisać swoją klasę do obsługi tych konkretnych plików, dziedzicząc z bazowej klasy TStream, nadpisując metody takie jak Read i Write oraz definiując własne. Wtedy zamiast użerać się z osobnym obiektem strumienia i globalnymi procedurami, można by ładnie operować na czytelnych metodach jednego obiektu.

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