AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Gedcom-Datei parsen

Ein Thema von hansklok · begonnen am 11. Dez 2014 · letzter Beitrag vom 7. Apr 2018
Antwort Antwort
Seite 2 von 4     12 34      
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.070 Beiträge
 
Delphi 10.4 Sydney
 
#11

AW: Gedcom-Datei parsen

  Alt 11. Dez 2014, 17:22
So richtig hilft mir der Gedankenansatz nicht weiter
Was ist denn dein konkretes Problem?
Suchst du nach der passenden Baum- und/oder Klassenstruktur?
  Mit Zitat antworten Zitat
hansklok

Registriert seit: 14. Apr 2004
Ort: Karlsruhe
318 Beiträge
 
Delphi 2010 Architect
 
#12

AW: Gedcom-Datei parsen

  Alt 11. Dez 2014, 17:42
So richtig hilft mir der Gedankenansatz nicht weiter
Was ist denn dein konkretes Problem?
Suchst du nach der passenden Baum- und/oder Klassenstruktur?
Die Klassenstruktur habe ich. Ich habe sie nach der Gedcom 5.51 Definition erstellt.

Das war an sich auch nicht das Problem, da ich ja nun die Datenstruktur/Modell habe. Es geht einzig und allein darum, wie ich eine bestehende Gedcom-Datei schnell einlesen kann. Und ich befürchte, dass das bei den ganzen Stringoperationen und bei Gedcom-Dateien mit 10000 Personen und/oder Familien und mehr etc. gar nicht so einfach ist, einen guten, schnellen Algorithmus zum parsen zu schreiben.
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#13

AW: Gedcom-Datei parsen

  Alt 11. Dez 2014, 18:16
Hier mal so ein Minimalprogramm zum Veranschaulichen
Delphi-Quellcode:
program dp_183093;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.Generics.Collections,
  System.SysUtils;

type
  TNode = class
  private
    FParent: TNode;
    FChildren: TList<TNode>;
    function GetLastChild: TNode;
    function GetChildCount: Integer;
    function GetChild( Index: Integer ): TNode;
  public
    constructor Create( AParent: TNode );
    destructor Destroy; override;

    procedure AddChild( ANode: TNode );

    property ChildCount: Integer read GetChildCount;
    property Children[Index: Integer]: TNode read GetChild;

    property Parent: TNode read FParent;
    property LastChild: TNode read GetLastChild;
  end;

  TGedFile = class( TNode )
  private
    function GetParent( Index: Integer ): TNode;
  public
    constructor Create( AValues: TArray<Integer> );
  end;

  { TNode }

procedure TNode.AddChild( ANode: TNode );
begin
  if Assigned( ANode.Parent ) and ( ANode.Parent <> nil )
  then
    raise Exception.Create( 'Fehlermeldung' );

  ANode.FParent := Self;

  if not FChildren.Contains( ANode )
  then
    FChildren.Add( ANode );
end;

constructor TNode.Create( AParent: TNode );
begin
  inherited Create;
  FChildren := TObjectList<TNode>.Create;
  if Assigned( AParent )
  then
    AParent.AddChild( Self );
end;

destructor TNode.Destroy;
begin
  FChildren.Free;
  inherited;
end;

function TNode.GetChild( Index: Integer ): TNode;
begin
  Result := FChildren[Index];
end;

function TNode.GetChildCount: Integer;
begin
  Result := FChildren.Count;
end;

function TNode.GetLastChild: TNode;
begin
  Result := FChildren.Last;
end;

{ TGedFile }

constructor TGedFile.Create( AValues: TArray<Integer> );
var
  LValue: Integer;
begin
  inherited Create( nil );

  for LValue in AValues do
    begin
      TNode.Create( GetParent( LValue ) );
    end;
end;

function TGedFile.GetParent( Index: Integer ): TNode;
begin
  Result := Self;
  while Index > 0 do
    begin
      if not Assigned( Result )
      then
        raise Exception.Create( 'Fehlermeldung' );
      Result := Result.LastChild;
      Dec( Index );
    end;
end;

procedure OutputNode( ANode: TNode; ALevel: Integer = 0 );
var
  LIdx: Integer;
begin
  Write( '':ALevel, ANode.ToString );
  if ALevel > 0
  then
    Write( ' (', ALevel - 1, ')' );
  WriteLn;
  for LIdx := 0 to ANode.ChildCount - 1 do
    OutputNode( ANode.Children[LIdx], ALevel + 1 );
end;

procedure Main;
var
  LFile: TGedFile;
begin
  LFile := TGedFile.Create( TArray<Integer>.Create( 0, 1, 2, 3, 1, 1, 1, 2, 3, 3, 3, 3 ) );
  try
    OutputNode( LFile );
  finally
    LFile.Free;
  end;
end;

begin
  try
    Main;
  except
    on E: Exception do
      WriteLn( E.ClassName, ': ', E.Message );
  end;
  ReadLn;

end.
Ausgabe ist dann wie folgt
Code:
TGedFile
 TNode (0)
  TNode (1)
   TNode (2)
    TNode (3)
  TNode (1)
  TNode (1)
  TNode (1)
   TNode (2)
    TNode (3)
    TNode (3)
    TNode (3)
    TNode (3)
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.070 Beiträge
 
Delphi 10.4 Sydney
 
#14

AW: Gedcom-Datei parsen

  Alt 11. Dez 2014, 18:28
Und ich befürchte, dass das bei den ganzen Stringoperationen und bei Gedcom-Dateien mit 10000 Personen und/oder Familien und mehr etc. gar nicht so einfach ist, einen guten, schnellen Algorithmus zum parsen zu schreiben.
Dann dauert das eben ein bisschen beim Einlesen.
Wie oft man das für eine GEDCOM-Datei? => Einmal!
Das reine GEDCOM auslesen ist ja nicht das Problem, oder?

Viel schwieriger stelle ich mir vor, die logische Verknüpfung und Visualisierung hinzugekommen.
Meine Ur-ur-ur-Großeltern waren beispielsweise entfernte Cousins und hatten einen gemeinsamen Ur-ur- bzw. Ur-ur-ur-Großvater.
Wie erkennt man solche "geschlossenen Kreise" im Stammbaum?
Gerade bei 10.000 Personen, die sonst wie miteinander verwandt sein können, hast du zwangsläufig die unmöglichsten Konstellationen.
  Mit Zitat antworten Zitat
hansklok

Registriert seit: 14. Apr 2004
Ort: Karlsruhe
318 Beiträge
 
Delphi 2010 Architect
 
#15

AW: Gedcom-Datei parsen

  Alt 11. Dez 2014, 19:33
[QUOTE=TiGü;1283119]
Viel schwieriger stelle ich mir vor, die logische Verknüpfung und Visualisierung hinzugekommen.
Meine Ur-ur-ur-Großeltern waren beispielsweise entfernte Cousins und hatten einen gemeinsamen Ur-ur- bzw. Ur-ur-ur-Großvater.
Wie erkennt man solche "geschlossenen Kreise" im Stammbaum?
Gerade bei 10.000 Personen, die sonst wie miteinander verwandt sein können, hast du zwangsläufig die unmöglichsten Konstellationen.
Genau das ist der Grund, warum ich mich so schwer tue, alles in einer Binärbaum-Struktur unterzubringen.

Die Personen/Familien werden alle in einer eigenen Liste gespeichert. Auf sie zugreifen kann man über deren UUID. Dann ist es auch möglich Spezialfälle wie Inzucht oder Cousin- & Cousinenehen darzustellen.
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#16

AW: Gedcom-Datei parsen

  Alt 11. Dez 2014, 19:50
Die GEDCOM-Datei ist aber nun mal in einer Baum-Struktur abgelegt. Um das vernünftig aufbrechen zu können packt man den Dateiinhalt in so einen Baum und kann sich dann aus diesem Baum einfach bedienen.

Der Baum ist nur für das Einlesen gedacht. Kann aber, wenn der Baum durch die Daten befüllt wird auch dazu dienen wieder eine GEDCOM-Datei zu erstellen.

Das muss man ganz klar trennen.

Beispiel der Knoten NOTE kann Unterknoten CONT und der Unterknoten CONC haben. Da alle zugehörigen aber an NOTE hängen, kann ich aus dem Knoten NOTE den gesamten Text auslesen und in ein Memo-Feld einer Datenbank speichern.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
hansklok

Registriert seit: 14. Apr 2004
Ort: Karlsruhe
318 Beiträge
 
Delphi 2010 Architect
 
#17

AW: Gedcom-Datei parsen

  Alt 11. Dez 2014, 19:58
OK, soweit habe ich das verstanden.

Ich lese die Datei via TextInputStream ein (in Delphi TStringList) - und zwar Zeilenweise. Ich überprüfe, ob die Zeile mit 0 beginnt und extrahiere dann den Typ. Soweit klappt das.
Wenn Das Objekt vom z.B. Typ "INDI" ist, Erstelle ich dementsprechend eine neues IndividualRecord und rufe dessen Parser-Methode auf. Auch das klappt. ABER, und jetzt wieder zum Algorithmus, wie kann ich die Folgezeilen auslesen, ohne, dass ich unendlich viele und unnötige Schleifen aufmache?
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#18

AW: Gedcom-Datei parsen

  Alt 11. Dez 2014, 23:35
Also das hier lädt mir eine komplette StressTest-Demo-Datei ein
Delphi-Quellcode:
procedure TGedFile.LoadFromFile( const Filename: string );
var
  LValue: TDataRecord;
  LCurrent: TNode;
  LCurrentIdx: Integer;
  LLines: TStringList;
  LLine: string;
begin
  LCurrent := Self;
  LCurrentIdx := 0;

  LLines := TStringList.Create;
  try

    LLines.LoadFromFile( Filename );

    for LLine in LLines do
      begin

        LValue := ParseLine( LLine );

        while LCurrentIdx <> LValue.Level do
          begin
            if LCurrentIdx < LValue.Level
            then
              begin
                LCurrent := LCurrent.LastChild;
                Inc( LCurrentIdx );
              end
            else
              begin
                LCurrent := LCurrent.Parent;
                Dec( LCurrentIdx );
              end;
          end;

        if LValue.DataIsReference
        then
          LCurrent := TRefNode.Create( LCurrent, TNodeType.Create( LValue.NodeTypeStr ), LValue.Data, FReferenceDict )
        else
          LCurrent := TDataNode.Create( LCurrent, TNodeType.Create( LValue.NodeTypeStr ), LValue.Data );

        Inc( LCurrentIdx );

        if not LValue.Reference.IsEmpty
        then
          FReferenceDict.Add( LValue.Reference, LCurrent );
      end;

  finally
    LLines.Free;
  end;

end;

function TGedFile.ParseLine( const ALine: string ): TDataRecord;
var
  LValues: TArray<string>;
  LPrefix: string;
begin
  LValues := ALine.Split( [' '], 3 );
  Result.Level := LValues[0].ToInteger;

  // Reference gefunden?
  if LValues[1].StartsWith( '@' )
  then
    begin
      Result.Reference := LValues[1];
      Result.NodeTypeStr := LValues[2];
    end
  else
    begin
      Result.Reference := '';
      Result.NodeTypeStr := LValues[1];
      SetLength( LValues, 2 );
    end;

  LPrefix := string.Join( ' ', LValues );

  Result.Data := ALine.Substring( LPrefix.Length + 1 );
  Result.DataIsReference := Result.Data.StartsWith( '@' );
end;
Damit habe ich die im Speicher und kann auf jeden Knoten ganz gemütlich zugreifen um die Daten nun auszulesen und sonstwie zu verarbeiten. Die erzeugten Nodes parsen hier gar nichts ... wozu auch
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
hansklok

Registriert seit: 14. Apr 2004
Ort: Karlsruhe
318 Beiträge
 
Delphi 2010 Architect
 
#19

AW: Gedcom-Datei parsen

  Alt 12. Dez 2014, 00:04
Also das hier lädt mir eine komplette StressTest-Demo-Datei ein
Kannst Du den Quellcode bitte bisschen kommentieren? Was ist bei Dir TDataRecord?

Was macht LValue.DataIsReference?

Geändert von hansklok (12. Dez 2014 um 00:07 Uhr)
  Mit Zitat antworten Zitat
hansklok

Registriert seit: 14. Apr 2004
Ort: Karlsruhe
318 Beiträge
 
Delphi 2010 Architect
 
#20

AW: Gedcom-Datei parsen

  Alt 12. Dez 2014, 00:52
Also das hier lädt mir eine komplette StressTest-Demo-Datei ein
Kannst Du bitte deinen gesamten Source posten, dann kann ich ersten besser nachvollziehen und zweitens versuchen in Xoxo zu übersetzen, an sich nicht schwer.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 4     12 34      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:12 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz