Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   String nach bestimmten muster auflösen (https://www.delphipraxis.net/186985-string-nach-bestimmten-muster-aufloesen.html)

Overphil 19. Okt 2015 11:07

String nach bestimmten muster auflösen
 
Hallo zusammen, ich habe folgendes Problem.
Ich will ein Programm schreiben, mit dem ich mittels eigenen Textbefehlen aus einem editfeld in einer Imagekomponente zeichnen kann. Also z.B. V90R45V90 soll 90px "nach vorne" zeichnen, dann wird um 45 grad nach rechts gedreht und wieder 90px in die neue richtung gezeichnet.
Außerdem gibt es noch wiederholungsbefehle, die zb so aussehen könnten: W4(v90r45). In dem fall soll das in der klammer 4 mal wiederholt werden. Mein problem ist jetzt das umschreiben dieser wiederholungsstrings. Mein ansatz war, den inhalt der klammern in einen neuen string zu speichern und diesen dann eben entsprechend oft zu wiederholen. Durch eine while schleife ermittle ich, wo sich die erste und zweite klammer des jeweiligen W befindet und speichere dann den Inhalt in einem neuen string. Meine frage also jetzt, wie mache ich es, dass der string so lange aufgelöst wird, bis er keine wiederholungsbefehle mehr enthält, denn so ein string könnte ja auch so aussehen W2(w2(v90)), wenn man das auflöst, hätte man ja w2(v90)w2(v90), was wiederrum aufgelöst werden muss.
Hier meine kleine Funktion, die den string auflösen soll:

Meine Variablen
repInt: gibt die Anzahl der wiederholungen an, die nach einem W steht
wCount: Variable zum finden der zugehörigen klammer
bracketPlace1/2: Position der ersten bzw. zweiten Klammer
repStr: Der string, der wiederholt werden soll(dieser soll eigentlich dann voll aufgelöst und der rückgabestring sein)
oldRepStr: der wiederholungsstring vom letzten schleifendurchlauf
strToCopy: beinhaltet den befehl in einer klammer


Code:
function TForm1.interpretateString(str:string) : string;
var repInt, wCount, bracketPlace1, bracketPlace2, i, j: integer;
  repStr, oldRepStr, strToCopy: string;
begin
     wCount:=0;
     bracketPlace2:=0;
     oldRepStr:='';
     repStr:='';
     for i:=1 to Length(str) do begin
         if str[i]='W' then begin



              repInt:=getCount(str, i);

              bracketPlace1:=i+Length(IntToStr(repInt))+2;
              j:=bracketPlace1;
              while wCount<>-1 do begin
                    if str[j]=')' then begin
                       wCount:=wCount-1;
                       if wCount=-1 then begin
                          //Delete(str, j, 1);
                          bracketPlace2:=j+1;
                          break;
                       end;
                    end
                    else if str[j]='(' then begin
                         wCount:=wCount+1;
                    end;

                   j:=j+1;
              end;

              strToCopy:=Copy(str, bracketPlace1, bracketPlace2-bracketPlace1-1);


              repStr:=repStr+repeatString(strToCopy, repInt);


              ShowMessage('repStr: '+repstr);
              oldRepStr:=repStr;

              ShowMessage('ors... '+ oldRepStr);

              result:=repStr+oldRepStr;

         end;
     end;

end;
PS: Ich weiß, dass manches da nach der while schleife nicht viel sinn gibt, aber das ist eben meine frage, wie ich nach der schleife weiter mache, sodass alles aufgelöst in einem string gespeichert wird.

Danke im vorraus

himitsu 19. Okt 2015 13:00

AW: String nach bestimmten muster auflösen
 
Der einfachste Weg ist rekursiv.

Du zerlegst den Befehl und bei einer Klammer nimmst du den Zeil zwischen den Klammern und übergibst ihn ebenfalls an die Parserfunktion.

Zwischen = Klammern zählen bei ( +1, bei ) -1 und wenn 0, dann ist das die zugehörige schließende Klammer.

Bei W2 wird der Teilstring dann halt doppelt ausgewertet.


Ansonsten lernst du was eine Iteration ist und baust die Rekursion entsprechend um.

Rollo62 19. Okt 2015 13:42

AW: String nach bestimmten muster auflösen
 
Nur mal am Rande;

Warum nimmst du nicht die gleichen Zeichenbefehle wie in TPathData ?
Könnte sein das dies so komplett definiert ist.

Bin zwar nicht sicher für alle Elemente, aber zumindest LineTo und MoveTo wird genauso wie in Inkscape geschrieben.
Ich würde einfach mal einen Path in Inkscape schreiben so wie du ihn gerne hättest, und dann mal analysieren
wie dies in Xml kodiert ist.
Wahrscheinlich macht eine solche "Zeichensprache" mehr Sinn wenn sie möglichst kompatibel ist (Inkscape, FireMonkey TPathData).

Rollo

hathor 19. Okt 2015 14:58

AW: String nach bestimmten muster auflösen
 
Kennst Du TURTLE ?
http://www.hsg-kl.de/faecher/inf/mat...ten/turtle.php

https://de.wikipedia.org/wiki/Turtle-Grafik

BUG 19. Okt 2015 15:37

AW: String nach bestimmten muster auflösen
 
Zitat:

Zitat von Rollo62 (Beitrag 1319073)
Warum nimmst du nicht die gleichen Zeichenbefehle wie in TPathData ?

Ich vermute, dass das ein krudes proprietäres Format ist, das Overphil unterstützen muss :wink:

Langfristig würde ich darüber nachdenken, die Stringersetzung durch einen AST zu ersetzen. Du hast Objekte, die einzelnen Syntaxelemente repräsentieren: in deinem Fall Befehle und Schleifen.
Mit einem Visitor kannst du das Ganze dann ausführen, in ein anderes Format exportieren, oder auch einfach die Schleifen entrollen.

Overphil 19. Okt 2015 20:31

AW: String nach bestimmten muster auflösen
 
So, hab es mal mit der Rekursion versucht, allerdings klappt das ganze noch nicht so. Es wird jetzt nur die erste Klammerebene aufgelöst, aber sobald eine wiederholung in einer klammer ist klappt das nicht mehr.

Code:
function TForm1.getRepStr(str:string; count:integer) : string;
var repInt, bracketPlace1, bracketPlace2, i, wCount : integer;
  repStr : string;
begin

     wCount:=0;
     repInt:=getCount(str, count);
     bracketPlace1:=count+Length(IntToStr(repInt))+2;

     i:=bracketPlace1;
     while wCount<>-1 do begin
           if str[i]=')' then begin
              wCount:=wCount-1;
              if wCount=-1 then begin
                 bracketPlace2:=i+1;
                 break;
                 end;
              end
              else if str[i]='(' then begin
                   wCount:=wCount+1;
              end;
     i:=i+1;
     end;

     repStr:=Copy(str, bracketPlace1, bracketPlace2-bracketPlace1-1);

     repStr:=repeatString(repStr, repInt);
     ShowMessage('repStr: ' + repStr);
     if containsW(repStr)=true then repStr:=getRepStr(repStr, 1)
     else result:=repStr;

end;

Zacherl 20. Okt 2015 00:25

AW: String nach bestimmten muster auflösen
 
Irgendwie so in der Art könnte es aussehen:
Delphi-Quellcode:
function TForm2.ParseString(Root: TTreeNode; const S: String): Integer;
var
  I, J: Integer;
  T: TTreeNode;
begin
  I := 1;
  while (I <= Length(S)) do
  begin
    if (S[I] in ['W', 'V', 'R']) then
    begin
      J := I + 1;
      while (J <= Length(S)) and (S[J] in ['0'..'9']) do
      begin
        Inc(J);
      end;
      T := TreeView1.Items.AddChild(Root, Copy(S, I, J - I));
      if (S[I] = 'W') then
      begin
        Inc(I, J - I + ParseString(T, Copy(S, J + 1, Length(S))));
      end else
      begin
        Inc(I, J - I);
      end;
    end else if (S[I] = ')') then
    begin
      Inc(I);
      Break;
    end else
    begin
      raise Exception.Create('Failed to parse string.');
    end;
  end;
  Result := I;
end;

procedure TForm2.Button1Click(Sender: TObject);
begin
  TreeView1.Items.Clear;
  TreeView1.Items.BeginUpdate;
  try
    ParseString(nil, AnsiUpperCase('V900W2(w2(v90))V90W5(V45)'));
  finally
    TreeView1.Items.EndUpdate;
  end;
end;
Geht auf jeden Fall noch eleganter, aber die Lösung hier sollte recht einfach zu verstehen sein. Die Funktion zerlegt den String in seine Einzelteile und ordnet die Befehle dann in einen TreeView ein.

Dejan Vu 20. Okt 2015 06:30

AW: String nach bestimmten muster auflösen
 
Hier mal Pseudocode.
Delphi-Quellcode:
procedure Parse;
begin
  While GetNextCommand(command) do
    Execute(command);
end;

Function GetNextCommand(var cmd : TCommand) : Boolean;
var
  c : Char;

Begin
   if AtEof Then Exit(False)
   c := TakeChar;
   if c=')' then Exit(False);
   if not (c in allowedCommands) then raise Exception.Create('Invalid command');
   cmd := TCommand.Create;
   cmd.Command := c;
   cmd.Value := TakeNumber;
   if (cmd.Command = RepeatCmd) then
     if TakeChar<>'(' then Raise Exception.Create('Expected "("')
   result := True;
End;

Procedure Execute (aCmd : TCommand);
Begin
  case aCmd.Command of
    RepeatCmd : Parse;
    ForwardCmd : GoForward (aCmd.Value);
    RightCmd : TurnRight(aCmd.Value);
    ...
  end
end;

Function AtEof : Boolean;
Begin
  result := CurrentPtr>Length(InputString);
End;

Function TakeChar : Char;
Begin
  result := InputString[CurrentPtr];
  inc(CurrentPtr);
End;

Function TakeNumber : Integer;
Var
  p : Integer;

Begin
  p := CurrentPtr;
  While TakeChar in Digits and Not AtEof;
  if p=CurrentPtr then Raise Exception.Create('Not a number');
  result := StrToInt(Copy(InputString,p,CurrentPtr-p));
end;

Procedure Run (aString : String);
Begin
  InputString := aString;
  CurrentPtr := 1;
  Parse;
End;
Ganz grob skizziert.

Overphil 20. Okt 2015 11:57

AW: String nach bestimmten muster auflösen
 
Danke erstmal für die vielen Antworten, das mit dem TreeView klappt zwar gut, doch wie mache ich es jetzt, dass ich eine Funktion habe, der man einen string mitgibt und dann den fertigen aufgelösten string wieder zurückgibt?

Zacherl 20. Okt 2015 16:05

AW: String nach bestimmten muster auflösen
 
Zitat:

Zitat von Overphil (Beitrag 1319187)
Danke erstmal für die vielen Antworten, das mit dem TreeView klappt zwar gut, doch wie mache ich es jetzt, dass ich eine Funktion habe, der man einen string mitgibt und dann den fertigen aufgelösten string wieder zurückgibt?

Warum willst du den Tree denn überhaupt wieder in einen String auflösen? Du kannst doch jetzt wunderbar über die Struktur traversieren und die darin enthaltenen Befehle direkt ausführen.

Auf die Schnelle aus dem Kopf:
Delphi-Quellcode:
procedure Execute(Node: TTreeNode);
var
  S: String;
  I, J: Integer;
begin
  S := Node.Text;
  if (S[1] = 'W') then
  begin
    for I := 1 to {Anzahl an Iterationen} do
    begin
      for J := 0 to Node.Childs.Count - 1 do
      begin
        Execute(Node.Childs[J]);
      end;
    end;
  end else if (S[1] = 'V') then
  begin
    // ..
  end else if (S[1] = 'R') then
  begin
    // ..
  end;
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:54 Uhr.
Seite 1 von 2  1 2      

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