2010-07-08 8 views
8

Ich habe ein seltsames Problem bei der Konvertierung von Code von Delphi 7 nach 2010. Es hat mit Datensätzen zu tun. Der unten definierte Datensatz ist in D7 432 Bytes und in D2009 (und 2010) 496. Ich weiß, dass es eine einfache Lösung ist, einen gepackten Datensatz zu erstellen, dann kommen alle Versionen auf 426 Bytes ... Wir haben jedoch Daten gespeichert, wo wir den Datensatz gestreamt haben und jetzt versuchen wir, diese Streams mit einer neueren Sprache zu lesen.Delphi 7 im Vergleich zu 2009 (& 2010) Rekordgrößen

Bei der Untersuchung dieses Problems habe ich einen anderen Datensatz erstellt, und aus welchen Gründen auch immer sind die Größen gleich? Der Datensatz ist kleiner, hat jedoch die gleichen Datentypen. aber es kommt in allen Versionen der Sprache gleich groß raus.

TMyRecord = Record 
Ext1 : Extended; 
Ext2 : Extended; 
Ext3 : Extended; 
Ext4 : Extended; 
Ext5 : Extended; 
Ext6 : Extended; 
Int1 : Integer; 
Int2 : Integer; 
char1 : AnsiChar; 
char2 : AnsiChar; 
MyString : String[15]; 
Arr1 : Array[1..3] of Extended; 
Arr2 : Array[1..3] of Extended; end; 

Jeder hat einen Einblick, warum ein Datensatz so anders ist, und der andere ist der gleiche? Etwas, was mit den Randlinienausrichtungen in Delphi zu tun hat. aber was hat sich von einer version zur nächsten so drastisch geändert?

+0

Ich weiß, dass die Standard-Byte-Ausrichtung für Delphi-Datensätze in einer aktuellen Version (2009 denke ich) geändert, aber ich bin mir nicht sicher über die Details. –

Antwort

2

Ich glaube, die Standardausrichtung wurde breiter gemacht. Geben Sie Ausrichtung 4 in den späteren Versionen an, und prüfen Sie, ob die Ausrichtung Ihren Vorstellungen entspricht.

Für die Zukunft sollten Sie sicherstellen, dass jeder Datensatz, der auf die Festplatte geschrieben wird, gepackt gespeichert wird, damit Sie nicht auf diese Weise gebrannt werden.

Edit: Da keine der Alignments funktionieren (was mich überrascht) würde ich zum Original zurückkehren und herausfinden, wie es wirklich ausgerichtet ist. Füllen Sie den Datensatz mit etwas wie $ FF, legen Sie die Daten und schreiben Sie es aus - sehen Sie, wo die $ FFs überlebten. Nehmen Sie den neuen Rekord, machen Sie ihn gepackt und fügen Sie Füllstoffe hinzu, um die Auffüllung im alten Rekord zu treffen.

Eine Sache: Ist das eigentlich nur ein Rekord? In den alten Tagen habe ich Objekte als gefälschte Datensätze mit Vererbung verwendet - oops, an der Stelle der Vererbung wurde die normale Ausrichtung angewendet und ich konnte es nicht stoppen. Ich musste dann vor den Daten auffüllen, damit die erzwungene Ausrichtung meine Daten nicht unterbricht. (Das ging zu einer API, es HAD richtig zu sein, konnte ich die Felder nicht unabhängig verarbeiten.)

+0

Wie kann man "Ausrichtung" angeben? –

+0

Gefunden: {$ A4}. Aber es hat nicht geholfen. Die Nummer ist jetzt 464. Ich habe alle anderen {$ A?} Möglichkeiten ausprobiert. –

14

Nun, das erste Problem ist, dass Sie einen nicht gepackten Datensatz auf der Festplatte gespeichert. Das Packen von Feld und Array kann zwischen Produktfreigaben geändert werden, da das Layout im Speicher normalerweise außerhalb des Prozesses nicht sichtbar ist. Du hast diese Regel gebrochen.

Wenn die Byte padding Standardwerte geändert zwischen Delphi 7 und Delphi 2009, herauszufinden, was die Standardwerte in D7 waren und die Standardwerte auf den gleichen in Delphi 2009

Auch gesetzt überprüfen Sie die Array Verpackung Standard. Ich kann mich nicht erinnern, ob es dafür eine separate Einstellung gibt.

Sehen Sie sich Ihre Datensatzstruktur in Delphi 2009 in der Debugspeicheransicht an. Einige oder alle der zusätzlichen Größen können auf das Auffüllen des Datensatzes selbst zurückzuführen sein (nicht auf die Felder darin), so dass die Array-Elemente bei der Verwendung des Datensatzes in einem Array auf schnellen Maschinengrenzen liegen.

Wenn nichts davon hilft, erstellen Sie einen temporär gepackten Datensatztyp in D2009 und fügen Sie manuell Byte-Füllfelder zwischen den tatsächlichen Datenfeldern ein, bis die Datensatzgröße und Feldausrichtung dem D7-Layout entsprechen. Es ist nicht nur Größe, es ist Feldausrichtungen. Lesen Sie Ihre alte Datendatei mit diesem temporär gepackten Datensatz. Übertragen Sie dann die Daten Feld für Feld in D2009 in Ihren "echten" Satztyp und schreiben Sie eine neue Datei aus.

Und während Sie dabei sind, packen Sie diesen Datensatz in D2009, so dass dies nicht wieder passiert.

+4

Das geht davon aus, dass Sie nicht die Möglichkeit haben, zu D7 zurückzukehren und dort den Code zu ändern, um die Datensatzstruktur zu packen und eine Datei mit gepackten Datensätzen auszugeben. Das wäre weniger Arbeit, als manuell zu versuchen, das Byte-Padding nach der Tatsache abzugleichen. – dthorpe

+0

Hind Sicht ist immer 20/20 ... und vorausschauende Entwicklung (und Entwickler) passiert nicht immer. Gute Idee über die gepackte Platte mit Räumen für 2009/2010. Wir werden es versuchen. –

+8

Im Nachhinein sollte auch lehrreich sein. Entpackte Datensätze in eine Datei schreiben sollte nur einmal pro Karriere passieren. ;> – dthorpe

7

Ich glaube, Sie haben eine Funktion getroffen! Ich konnte keine vernünftige Größe mit Ihrem TToTry mit D2007 bekommen, also musste ich Feldadressen mit dem Debugger nachschlagen;

Zuerst Größe des Datensatzes unten,

{$A8} 
type 
    TToTry = record 
    j: array[1..3] of Extended; 
    k: array[1..3] of Extended; 
    l: array[1..3] of Extended; 
    m: array[1..3] of Extended; 
    end; 

ist (32 * 4). Dies ist zu erwarten, da ein Extended 10 Bytes ist, 30 Bytes würden auf 32 Bytes ausgerichtet sein.

Aber Größe dieses Datensatzes,

{$A8} 
type 
    TToTry = record 
    j, k, l, m: array[1..3] of Extended; 
    end; 

ist (30 * 4). Dies ist sicherlich unerwartet - Felder sollten immer noch auf einer 8-Byte-Grenze ausgerichtet sein.

(ich weiß nicht D7 aber mein Denken ist, um zu überprüfen haben, dass :)
So, jetzt wissen wir, dass gruppierte Felder verpackt sind, folgt daraus, dass die Ausrichtung auf D7 8 Bytes ist und Ihr Datensatz wird fast gepackt;

TToTry = Record 
a,b,c,d : Extended; // 40 bytes (8*5) 
e,f,g,h : Extended; // 40 bytes (8*5) 
i : String[15];  // 16 bytes (8*2) 
j,k,l,m,n,o,p,q,r,s,t: Array[1..3] of Extended; // 330 bytes 
End; 

Der Compiler ist Klotzen 6 Bytes der letzten Gruppe er ein Vielfaches von 8 zu haben, und dann erhält man 40 + 40 + 16 + 336 = 432 Bytes.

Mit D2009/D2010 deklarieren Sie entweder jedes Feld - ohne sie zu gruppieren oder das Verhalten wird geändert. So oder so Pack Ihre Aufzeichnung und fügen Sie ein 6-Byte-Array Dummy-Feld zum Ende, und Sie sollten gut zu gehen. Wenn das nicht funktioniert, sehen Sie sich die Feldadressen Ihres Datensatzes mit D7 an, erstellen Sie dann ein exaktes Duplikat auf D2009, indem Sie einen gepackten Datensatz verwenden und bei Bedarf Dummy-Felder verwenden, nachdem Sie Ihre gespeicherten Daten importiert haben lösche die Dummy-Felder.

-
Ich habe nie ein solches Verhalten gewusst und ich kann es nirgendwo dokumentiert finden. Dennoch ist es so etwas wie ein Feature, dass ich zögere es einen Fehler zu nennen. Ich weiß nicht, ob das Verhalten bei D2009 oder D2010 das gleiche ist. Testen Sie, ob es so ist. Wenn dies der Fall ist, um erwartete Ergebnisse zu erhalten - keine halbgepackten Datensätze zu haben -, seien Sie nicht faul und deklarieren Sie jedes einzelne Feld für nicht gepackte Datensätze.

+0

Die Größe des zweiten Datensatzes in Delphi 2009 ist 128 Byte – kludg

+0

@Serg - danke für das Nachschlagen, also ist es kein Feature. Tatsächlich stieß ich beim Testen mit D2007 auf andere seltsame/unerwartete Ausrichtungen. Ich bin froh, dass spätere Versionen es gerade haben! –

2

Das Anwenden der Anweisung {$ A8} bedeutet nicht, dass alle Datensatzfelder an der 8-Byte-Grenze ausgerichtet sind - der Compiler verwendet eine andere Ausrichtungsstrategie. Zum Beispiel kann die Größe der

{$A8} 
type 
    TToTry = record 
    a: byte; 
    b: word; 
    c: longword; 
    end; 

ist 8 Bytes in Delphi 2009, weil der Compiler 2-Byte-Wert in dem 2-Byte-Grenze ausgerichtet ist, 4-Byte-Wert an den 4-Byte-Grenze, und die einzigen tatsächliche Ausrichtung in Das obige Beispiel ist b-Feld an 2-Byte-Grenze ausgerichtet.

Was die ursprüngliche Frage, was zwischen Delphi 7 und Delphi 2009 geändert - Sertac Akyuz Antwort und meinem Kommentar, um es

+0

Ah, Ok! Das meinte ich mit "komisch/unerwartet" mit meinem Kommentar zu meinem Kommentar zu meiner Antwort. Aber ist das zu erwarten? Die Dokumente sagen: "Im Zustand {$ A8} oder {$ A +} werden Felder in Datensatztypen, die ohne den gepackten Modifizierer deklariert sind, und Felder in Klassenstrukturen auf Vierfachwortgrenzen ausgerichtet." –

+2

@Sertac: Quad-Word-Ausrichtung gilt nur für Datentypen, die mindestens 8 Byte lang sind. Bei der x86-Architektur ist wenig zu gewinnen, wenn ein Bytefeld an einer 8-Byte-Grenze platziert wird. (Es könnte mit Cache-Line-Kollisionen im L2-Cache helfen, aber das ist wirklich sehr gering). Wie Serg sagte, passen sich Datentypen an ihre natürliche Grenze (= Größe der Daten) bis zur $ A/n/Größe an. Beachten Sie, dass mehrere Ausrichtungspunkte berücksichtigt werden müssen: Offset der Felder vom Anfang der Struktur, Ausrichtung der Daten innerhalb der Arrays und Auffüllen des Datensatzes selbst, damit er gut ausgerichtet ist, wenn er in einem Array verwendet wird. – dthorpe

+1

@dthorpe - Danke, alle machen Sinn. Aber ich möchte, dass das Thema "strukturierte Typen" oder das Feld "Felder ausrichten" in der Dokumentation deutlicher wird, insbesondere für diejenigen wie mich, die keine Kenntnis der zugrunde liegenden Architektur haben. –

3

Ich weiß, das ist eine alte Post, aber ich lief in das gleiche Problem mit Delphi XE2 lesen gestern . Ich habe mehrere getippte Dateien, die mit einer Turbo Delphi 2006 App geschrieben wurden, und ich habe auch den Fehler gemacht, keine gepackten Datensätze zu verwenden.Meine Situation wurde komplizierter, weil ich Datensätze mit variabler Länge verwendete (so nennen wir sie in der z/OS-Welt, nicht 100% sicher von Delphi), sodass die falsche Ausrichtung dazu führte, dass die Datensatzschlüssel-Tags nicht eintrafen das korrekte Feld, wodurch keiner der Sub-Datensätze an der richtigen Stelle ist, wodurch die gesamte Datei unbrauchbar wird.

Was ich entdeckt habe, ist, dass Sie die Ausrichtung Compiler-Direktiven nur um einen bestimmten Block von Code setzen können. Zusammen mit, dass entdeckte ich auch dieses kleine Juwel: {$OLDTYPELAYOUT ON}

{$OLDTYPELAYOUT ON} 
RMasterRecord = Record  
    mKey  : word; 
    mDeleted : boolean; 
    case mType : ANSIChar of 
     'V' : //Info 
      (Vers : RVersionLayout); 
     […] 
{$OLDTYPELAYOUT OFF} 

ich die Richtlinie nur um den Stammsatz setzen, die und lesen in die Datei geschrieben wird, nicht um den Unterrekord Definitionen. Das hat mein Problem gelöst, und ich kann jetzt in XE2 kompilieren und meine TD2006-Dateien lesen. Das nächste Mal werde ich gepackte Datensätze verwenden (oder besser SQLite). Aber ich dachte, ich würde dies teilen, da diese Seite mir seit Jahren unermesslich geholfen hat.

Sie können mehr über $ OLDTYPELAYOUT hier lesen: http://docwiki.embarcadero.com/RADStudio/XE5/en/Internal_Data_Formats#Record_Types. Übrigens, der erste, der es reparierte, war {$A4}, aber als ich {$OLDTYPELAYOUT ON} entdeckte, schaltete ich es um, um zu verwenden, da es offensichtlich ist.

Verwandte Themen