Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi [TStringList] Problem mit einer Datei (Lädt nicht) (https://www.delphipraxis.net/78275-%5Btstringlist%5D-problem-mit-einer-datei-laedt-nicht.html)

xZise 2. Okt 2006 12:43


[TStringList] Problem mit einer Datei (Lädt nicht)
 
Liste der Anhänge anzeigen (Anzahl: 1)
Datei siehe Anhang
Ich habe eine Datei, die ich mit einer TStringList geschrieben habe. Nur komischerweise macht sie beim Laden zicke:

Delphi-Quellcode:
procedure TfrmMain.leseMap(Path : TFileName);
var
  i, j, k : Integer;
  F2M : TStrings;
  Buffer : Char;
begin
  // Map leeren:
  SetLength(Map, 0, 0, 0);

  F2M := TStringList.Create;
  try
    F2M.LoadFromFile(Path);
    if LowerCase(ExtractFileExt(Path)) = '.fmap' then begin
      {...}
    end else begin
      // Ansonsten normal "f2m"
      SetLength(Map, 4, Length(F2M[1]), Round(F2M.Count / 4));
      for i := MT_PLAYER to MT_KI do begin
        for j := 0 to Length(F2M[1]) - 1 do begin
          for k := 0 to Round(F2M.Count / 4) - 1 do begin
            Map[i, j, k] := Ord(F2M[i * Round(F2M.Count / 4) + k + 1][j + 1]);
          end;
        end;
      end;
    end;
  finally
    F2M.Free;
  end;
end;
Das funktioniert nicht, und ich bekomme im "SetLength" einen Fehler, weil er "F2M[0]/[1]" nicht kennt:
Zitat:

F2M[0]: Delphi exception EStringListError at $740F949
F2M[1]: Delphi exception EStringListError at $740F949
Und Path gibt es (direkt von OpenDialog ausgelesen!)...

[edit]OOPs... Da fehlte die Datei ^^[/edit]

marabu 2. Okt 2006 18:12

Re: [TStringList] Problem mit einer Datei (Lädt nicht)
 
Hallo Fabian,

was erwartest du? LoadFromFile() interpretiert deinen Dateiinhalt als einen String und will ihn an der Zeichenfolge sLineBreak in einzelne Zeilen zerlegen. Schon beim ersten Zeichen wird die Methode allerdings hart ausgebremst, denn ein low value (#0) signalisiert, dass der Text bereits zu Ende ist. Du solltest nur echte Texte in TStrings und seinen Nachkommen verstauen. Oder hast du dir deine Datei selbst noch gar nicht angesehen?

Grüße vom marabu

xZise 2. Okt 2006 19:15

Re: [TStringList] Problem mit einer Datei (Lädt nicht)
 
Also eigentlich hatte ich geplannt eine Zahl zw. 0 un 255 in ein ASCII-Zeichen zu ändern und dann abzuspeichern.

marabu 2. Okt 2006 19:42

Re: [TStringList] Problem mit einer Datei (Lädt nicht)
 
Ja mach doch. Nur ein Zeichen oder mehrere? Immer gleich viele? Beschreibe mal etwas mehr die Projektidee, bevor du ein Detail der Umsetzung diskutierst. Sonst bekommst du noch falsche Ratschläge.

Grüße, marabu

xZise 2. Okt 2006 19:56

Re: [TStringList] Problem mit einer Datei (Lädt nicht)
 
Okay!
Also ich möchte eine Karte laden.
Die Karte besteht aus X mal Y Feldern.
Inder Datei wäre das dann:
Breite = X
Höhe = Y * 4 (4 Ebenen)

Und die Eigenschafften jedes Feldes sind in einer Zahl definiert, welche ich (eigentlich) aus ein ASCII-Zeichen umwandeln wollte. Dummweise geht das aber nicht so, wie ich das wollte.

Ich hoffe mal es ist gut genug beschrieben.

marabu 2. Okt 2006 20:20

Re: [TStringList] Problem mit einer Datei (Lädt nicht)
 
Du kannst dein drei-dimensionales Array einfach binär abspeichern - alles auf einmal in einen FileStream. Oder willst du es lieber in einer lesbaren Textdatei speichern?

marabu

xZise 2. Okt 2006 20:50

Re: [TStringList] Problem mit einer Datei (Lädt nicht)
 
Also es wäre einfacher mit einer "String-Datei"... Müsste ich dann nur auf das 1. Zeichen verzichten? Oder wie sieht das aus?

[edit]OOPs... eigentlich sollte das genau andersherum sein: Ich wollte eine Map auslesen. :D[/edit]

xZise 27. Okt 2006 12:13

Re: [TStringList] Problem mit einer Datei (Lädt nicht)
 
Ich möchte nocheinmal zurückkommen ... *gg*

Also ich wollte eigentlich eine Datei bekommen, in der die ASCII-Werte vorkommen (0-255).
Die Größe ist wie oben beschrieben A * B Zeichen und die Kartengröße dann A * (B / 4).

Wie kann ich nun an die Daten kommen? Also, soweit ich das richtig erkannt habe, schafft er es ja immerhin :D die Werte abzupseichern ... Nur am auslesen haperts...

marabu 27. Okt 2006 12:47

Re: [TStringList] Problem mit einer Datei (Lädt nicht)
 
Hallo Fabian,

deine Datei Map1.f2m enthält 48 Datensätze mit jeweils 14 Zeichen. Der Inhalt der Datensätze ist binär, aber die letzten beiden Zeichen cr/lf lassen mich vermuten, dass du die Zeilen mit WriteLn() weggeschrieben hast. Das scheint mir in zweierlei Hinsicht nicht optimal zu sein. Warum mit WriteLn() schreiben, wenn du die Daten mit ReadLn() nicht zuverlässig wieder einlessen kannst? Außerdem musst du dein array doch gar nicht zerpflücken um es zu Speichern und wieder zu Laden:

Delphi-Quellcode:
type
  TMatrix = packed array [1..48, 1..12] of Byte;

procedure SaveMatrix(m: TMatrix; fn: TFileName);
begin
  with TFileStream.Create(fn, fmCreate) do
  try
    Write(m, SizeOf(m));
  finally
    Free;
  end;
end;

procedure LoadMatrix(var m: TMatrix; fn: TFileName);
begin
  with TFileStream.Create(fn, fmOpenRead or fmShareDenyWrite) do
  try
    Read(m, SizeOf(m));
  finally
    Free;
  end;
end;
Freundliche Grüße vom marabu

xZise 27. Okt 2006 21:12

Re: [TStringList] Problem mit einer Datei (Lädt nicht)
 
Also ich habe die Datei über "TStringList.SaveToFile(x)" gespeichert...

Nun die Fragen:
1. Was ist ein "packed" array? Kann es auch dynamisch sein?
2. Wie bekomme ich die Breite/Höhe?
3. Speichert es immer den ASCII-Wert? Also z.B. Chr(M[2][3])?

marabu 28. Okt 2006 09:25

Re: [TStringList] Problem mit einer Datei (Lädt nicht)
 
Guten Morgen,

auch die Methoden SaveToFile() und LoadFromFile() von TStrings arbeiten mit Textdateien - und mit dem Zeilentrenner CR/LF für solche Dateien. Du möchtest beliebige Zeichen (#0..#255) in deiner Datei speichern, wie man auch an deiner Musterdatei sehen kann. Kritisch sind dabei solche Zeichen wie #0 (NULL) #13 (CR) #10 (LF) und gelegentlich #26 (EOF), weil sie bei der Verarbeitung von Text als Steuerzeichen interpretiert werden.

Das Schlüsselwort PACKED kannst du mit strukturierten (record) und wiederholten (array) Feldern benutzen um die Ausrichtung einzelner Felder an Doppelwortgrenzen zu verhindern, was zwar das Laufzeitverhalten günstig beeinflusst, aber den Speicherverbrauch stark erhöht. Mir ist eine kompakte Speicherung wichtig, da ich die Matrix als Speicherblock behandeln möchte.

Wenn die Dimensionen deiner drei-dimensionalen Matrix nicht feststehen, dann musst du sie mit in die Datei schreiben, vorzugsweise als Header davor:

Delphi-Quellcode:
const
  LEVELS = 4;
  ROWS = 12;
  COLS = 12;

type
  TDimArray = array [0..3] of Byte;
  TMatrix = packed array of array of array of Byte;

procedure RedimMatrix(var m: TMatrix; dim: array of Byte);
begin
  SetLength(m, LEVELS, ROWS, COLS);
end;

procedure SaveMatrix(m: TMatrix; fn: TFileName);
var
  dwDim, dwSize: Cardinal;
begin
  dwDim := (Length(m) shl 8 + Length(m[0])) shl 8 + Length(m[0, 0]);
  dwSize := Length(m) * Length(m[0]) * Length(m[0, 0]);
  with TFileStream.Create(fn, fmCreate) do
  try
    Write(dwDim, SizeOf(dwDim));
    Write(m[0], dwSize); // korrigiert
  finally
    Free;
  end;
end;

procedure LoadMatrix(var m: TMatrix; fn: TFileName);
var
  dwDim, dwSize: Cardinal;
  dim: TDimArray absolute dwDim;
begin
  with TFileStream.Create(fn, fmOpenRead or fmShareDenyWrite) do
  try
    Read(dwDim, SizeOf(dwDim));
    // SetLength(m, Hi(dwDim), Lo(dwDim) shr 8, dwDim and $FF); // fehlerhaft
    SetLength(m, dim[2], dim[1], dim[0]);
    dwSize := Length(m) * Length(m[0]) * Length(m[0, 0]);
    Read(m[0], dwSize); // korrigiert
  finally
    Free;
  end;
end;

{
procedure TestMatrix;
const
  FN = 'c:\daten\dp\map.f2m';
var
  m: TMatrix;
  iLevel, iRow, iCol: Integer;
begin
  RedimMatrix(m, [LEVELS, ROWS, COLS]);
  for iLevel := Low(m) to High(m) do
    for iRow := Low(m[0]) to High(m[0]) do
      for iCol := Low(m[0, 0]) to High(m[0, 0]) do
        m[iLevel, iRow, iCol] := 48 + iLevel * 4 + iRow * iCol mod 10;
  SaveMatrix(m, fn);
  FillChar(m, SizeOf(m), 0);
  LoadMatrix(m, fn);
end;
}

procedure TestMatrix;
const
  FN = 'c:\daten\dp\map.f2m';
var
  m: TMatrix;
  iLevel, iRow, iCol: Integer;
  dwSize: Cardinal;
  okay: Boolean;
begin
  RedimMatrix(m, [LEVELS, ROWS, COLS]);
  dwSize := LEVELS * ROWS * COLS;
  for iLevel := Low(m) to High(m) do
    for iRow := Low(m[0]) to High(m[0]) do
      for iCol := Low(m[0, 0]) to High(m[0, 0]) do
        m[iLevel, iRow, iCol] := 48 + iLevel * 4 + iRow * iCol mod 10;
  SaveMatrix(m, fn);
  FillChar(m[0], dwSize, 0);
  LoadMatrix(m, fn);
  okay := True;
  for iLevel := Low(m) to High(m) do
    for iRow := Low(m[0]) to High(m[0]) do
      for iCol := Low(m[0, 0]) to High(m[0, 0]) do
        if m[iLevel, iRow, iCol] <> 48 + iLevel * 4 + iRow * iCol mod 10 then
          okay := False;
  WriteLn(IfThen(okay, 'fine', 'rats'));
end;
Freundliche Grüße

xZise 28. Okt 2006 10:19

Re: [TStringList] Problem mit einer Datei (Lädt nicht)
 
Zitat:

Zitat von marabu
Guten Morgen,

auch die Methoden SaveToFile() und LoadFromFile() von TStrings arbeiten mit Textdateien - und mit dem Zeilentrenner CR/LF für solche Dateien. Du möchtest beliebige Zeichen (#0..#255) in deiner Datei speichern, wie man auch an deiner Musterdatei sehen kann. Kritisch sind dabei solche Zeichen wie #0 (NULL) #13 (CR) #10 (LF) und gelegentlich #26 (EOF), weil sie bei der Verarbeitung von Text als Steuerzeichen interpretiert werden.

Achso ... Okay...

Zitat:

Zitat von marabu
Das Schlüsselwort PACKED kannst du mit strukturierten (record) und wiederholten (array) Feldern benutzen um die Ausrichtung einzelner Felder an Doppelwortgrenzen zu verhindern, was zwar das Laufzeitverhalten günstig beeinflusst, aber den Speicherverbrauch stark erhöht. Mir ist eine kompakte Speicherung wichtig, da ich die Matrix als Speicherblock behandeln möchte.

Verstehe ich noch net ganz ;)

Zitat:

Zitat von marabu
Wenn die Dimensionen deiner drei-dimensionalen Matrix nicht feststehen, dann musst du sie mit in die Datei schreiben, vorzugsweise als Header davor:

Indirekt weiß ich die Größe, da die Breite der Datei, die Breite der Map sein wird, und die Höhe der Datei, die Höhe der Map durch 4...

Also müsste ich nichts im Header schreiben ;) Obwohl ich dann das Problem bei #10 und #13 hätte, da die ja ein Zeilenumbruch verursachen... Also doch Header :D

Zitat:

Zitat von marabu
Delphi-Quellcode:
const
  LEVELS = 4;
  ROWS = 12;
  COLS = 12;

type
  TMatrix = packed array of array of array of Byte;

procedure RedimMatrix(var m: TMatrix; dim: array of Byte);
begin
  SetLength(m, LEVELS, ROWS, COLS);
end;

Das ist verstanden :D

Zitat:

Zitat von marabu
Delphi-Quellcode:
procedure SaveMatrix(m: TMatrix; fn: TFileName);
var
  dwDim, dwSize: Cardinal;
begin
  dwDim := (Length(m) shl 8 + Length(m[0])) shl 8 + Length(m[0, 0]);
  dwSize := Length(m) * Length(m[0]) * Length(m[0, 0]);
  with TFileStream.Create(fn, fmCreate) do
  try
    Write(dwDim, SizeOf(dwDim));
    Write(m, dwSize);
  finally
    Free;
  end;
end;

Ähm...
1. Was ist shl?
2. Soll dwDim der Header sein?
3. Was ist "SizeOf"? Soetwas wie "Length"?

Zitat:

Zitat von marabu
Delphi-Quellcode:
procedure LoadMatrix(var m: TMatrix; fn: TFileName);
var
  dwDim, dwSize: Cardinal;
begin
  with TFileStream.Create(fn, fmOpenRead or fmShareDenyWrite) do
  try
    Read(dwDim, SizeOf(dwDim));
    SetLength(m, Hi(dwDim), Lo(dwDim) shr 8, dwDim and $FF);
    dwSize := Length(m) * Length(m[0]) * Length(m[0, 0]);
    Read(m, dwSize);
  finally
    Free;
  end;
end;

Also das stößt schon auf weitere Verständnisprobleme:
1. dwDim hat ja noch keine Größe, aber warum funktioniert dann SizeOf?
2. Hi = High? Lo = Low? Oder was für Funktionen sind das?

Zitat:

Zitat von marabu
Delphi-Quellcode:
procedure TestMatrix;
const
  FN = 'c:\daten\dp\map.f2m';
var
  m: TMatrix;
  iLevel, iRow, iCol: Integer;
begin
  RedimMatrix(m, [LEVELS, ROWS, COLS]);
  for iLevel := Low(m) to High(m) do
    for iRow := Low(m[0]) to High(m[0]) do
      for iCol := Low(m[0, 0]) to High(m[0, 0]) do
        m[iLevel, iRow, iCol] := 48 + iLevel * 4 + iRow * iCol mod 10;
  SaveMatrix(m, fn);
  FillChar(m, SizeOf(m), 0);
  LoadMatrix(m, fn);
end;

Das ist wieder logisch! Danke erstmal dafür ;)

marabu 28. Okt 2006 11:01

Re: [TStringList] Problem mit einer Datei (Lädt nicht)
 
Hallo Fabian,

für die gängigen Mikro-Prozessoren sind Zugriffe auf Speicheradressen vorteilhaft, die ein Vielfaches der Registerbreite darstellen. Mit PACKED wird die vom Delphi-Compiler aus Performanzgründen (eventuell) vorgenommene Spreizung der einzelnen Felder eines strukturierten Datentyps verhindert.

Wenn die Dimensionen feststehen, dann musst du nicht mit einem dynamischen Array arbeiten. Den Header mit den Dimensionen würde ich wohl auch dann schreiben, aber die Redimensionierung in der Routine LoadMatrix() würde ich durch eine Dimensionsprüfung ersetzen.

Probleme mit #13 und #10 hast du immer - wenn du an der Textdatei festhalten willst.

"n SHL 8" ist das gleiche wie "n * 256" - oder auch "n * 2 hoch 8". Die Online-Hilfe von Delphi beschreibt die Operatoren SHL und SHR ganz gut.

dwDIM ist tatsächlich der Header - ein Vier-Byte-Wert, bei dem wegen deiner drei Dimensionen ein Byte nicht genutzt wird.

SizeOf(m) gibt den von m belegten Nettospeicher in Bytes an. Length(m) würde dir Größe der ersten Dimension des Arrays liefern.

SizeOf(dwSize) funktioniert also immer und liefert 4 - wie auch SizeOf(Integer).

Hi() und Lo() sind in meinem Code fehlerhaft angewendet, ich habe die Zeile geändert.


Grüße

Hawkeye219 28. Okt 2006 11:23

Re: [TStringList] Problem mit einer Datei (Lädt nicht)
 
Hallo marabu,

hast du übersehen, daß bei dynamischen Arrays nur ein Zeiger auf die Verwaltungsinformationen in der Referenzvariablen abgelegt wird? SizeOf(m) liefert also tatsächlich nur den Wert 4 - die Größe eines Pointers. Beim Aufruf von FileStream.Read, TFileStream.Write und FillChar muß also die Adresse des ersten Feldelements übergeben werden. Bei FillChar ist zusätzlich der zweite Parameter anzupassen.

Gruß Hawkeye

marabu 28. Okt 2006 11:46

Re: [TStringList] Problem mit einer Datei (Lädt nicht)
 
Hallo Hawkeye,

zwar habe ich nach der Umstellung meines Codes von statischem auf dynamisches Array die Routine SaveMatrix() angepasst, aber offensichtlich einige Stellen übersehen.

Freundliche Grüße

PS: Habe (hoffentlich) alle notwendigen Änderungen nachgetragen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:09 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz