Entwickler-Ecke

Sonstiges (Delphi) - String umdrehen


morphi - Fr 25.02.05 17:51
Titel: String umdrehen
Ich möchte den Text aus der Textbox Edit2 in Edit1 umgedreht haben also das aus Wurzel "lezruW" wird. z.B. ich bin anfäger

danke Morphi


delfiphan - Fr 25.02.05 17:57

:welcome:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
Var
 A, B : String;
 I : Integer;
begin
 A := 'Wurzel';
 B := A;
 For I := 1 to length(A) do
  B[length(A)-I+1] := A[I];
 // B := 'lezruW';
end;


uall@ogc - Fr 25.02.05 18:03


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
varr   
 A : String;   
 I : Integer; 
 B : char;   
begin   
 A := 'Wurzel';   
 For I := 1 to length(A) do 
 begin
  b := a[i];
  A[I] := A[length(A)-i+1]; 
  A[length(A)-i+1] := b;
 end;  
end;


für lange strings ist das besser da kein 2ter string angelegt werden muss


BeniSchindler - Fr 25.02.05 18:03

Oder :


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
Var
 A, B : String;
 Index : Integer;
begin
 A := 'Wurzel';
 B := '';
 For Index :=  length(A) downto 1 do B := B + A[Index];
 // B := 'lezruW';
end;


uall@ogc - Fr 25.02.05 18:12

das ist bis jetzt die schlechteste variante da delphi da immer einen neuen string erstellen muss


delfiphan - Fr 25.02.05 18:15

So werden ständig neue Strings aneinandergehängt :O

//Edit: Ok stimmt glaub ich nicht ganz.. :\ auf jeden Fall ist es performancemässig am besten, wenn du nicht ständig was zu einem String dazufügst. Wenn die Länge eines Strings am Anfang schon gegeben ist, dann solltest du das auch am Anfang festlegen.


jasocul - Fr 25.02.05 18:18

Dürfte noch schneller sein:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
Var
 A, B : String;
 Index : Integer;
 laenge : Integer;
begin
 A := 'Wurzel';
 laenge := Length(A); // Damit Length nur einmal aufgerufen werden muss
 SetLength(B,laenge);
 For Index := laenge downto 1 do B[laenge+1-Index] := A[Index];
end;


uall@ogc - Fr 25.02.05 18:20

jasocul
1.) hast du 2 strings -> das mag ich schon net ;>
2.) downto schleife langsamer als for to schleife
3.) speichert delphi die länge eh in einem register (ebx) von daher isses egal ob du das vorher in eine variable auslagerst


delfiphan - Fr 25.02.05 18:22

Wer hat Lust, das ganze jetzt noch in Assembler zu schreiben :D


delfiphan - Fr 25.02.05 18:26

Ich denke sowieso nicht, dass length(S) langsamer ist als ne gewöhnliche Variable zu lesen. Length wird nicht in einen call verwandelt, oder? Zu mindest nicht bei Pascal-Strings.


uall@ogc - Fr 25.02.05 18:27

length(s) wird bei einer schleifenvariable in EBX geschrieben, sollte also genausoschnell / shcneller sein
da delphi auch die variable in EBX packen würde


delfiphan - Fr 25.02.05 18:29

Also ich meinte eher grundsätzlich so... Bzw. beispielsweise in der For-Schleife drin:

Delphi-Quelltext
1:
2:
For I := 1 to length(A) do  
  B[length(A)-I+1] := A[I];


uall@ogc - Fr 25.02.05 18:32

meine variante hatte sogar noch nen bug


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
var s: string;
    i, l: integer;
    b: char;
begin
  s := 'das ist ein test';
  l := length(s);
  for i := 1 to l div 2 do
  begin
    b := s[i];
    s[i] := s[l-i+1];
    s[l-i+1] := b;
  end;
  form1.caption := s;
end;

ich muss nur die hälfte des strings machen ;>


jasocul - Fr 25.02.05 18:34

Neue Variante und Kommentare:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
Var
 A : String;
 B : Char;
 Index : Integer;
 Ende : Integer;
 laenge : Integer;
begin
 A := 'Wurzel';
 laenge := Length(A); // Damit Length nur einmal aufgerufen werden muss
 Ende := (laenge div 2)+1;
 For Index := laenge downto ende do begin
   B := A[laenge+1-Index];
   A[laenge+1-Index] := A[Index];
   A[Index] := B;
 end;
end;

Da ich die Länge mehrfach benötige macht es durchaus Sinn. Ich hätte allerdings auch auf A[0] zugreifen können. Da kann es aber zu Problemen mit AnsiStrings kommen.
Die neue Variante hat auch nur noch einen String.
Eine for..downto ist meistens schneller als for..to. Dazu gab es auch schon Threads, weil Delphi das auch in diese Richtung optimiert, wenn es möglich ist.
Die Schleife läuft jetz nur noch über die Hälfte des Strings.
Wenn jetzt noch was verbessert werden soll, läuft es auf Assembler raus.

//EDIT:
Mist. Hätte ich nicht kommentiert wäre ich schneller gewesen als uall@ogc :wink:


delfiphan - Fr 25.02.05 18:39

morphi hat folgendes geschrieben:
ich bin anfäger

Morphi wird Freude haben :)

Eben genau das war meine Frage: Ob denn length(S) in einen Function-Call oder bei normalen Strings direkt in ein S[0] übersetzt wird.

//Edit: Hui, ist aber schwach, es gibt tatsächlich einen Function Call. Hätte jetzt fast gedacht, dass das zu S[0] optimiert wird.


Sprint - Fr 25.02.05 18:41
Titel: Re: String umdrehen
morphi hat folgendes geschrieben:
Ich möchte den Text aus der Textbox Edit2 in Edit1 umgedreht haben also das aus Wurzel "lezruW" wird. z.B. ich bin anfäger


Delphi-Quelltext
1:
  Edit1.Text := AnsiReverseString(Edit2.Text);                    

Unter Delphi 7 gibt es die Funktion AnsiReverseString. Unter Delphi 5 nicht. Bei Delphi 6 weiß ich es nicht.


jasocul - Fr 25.02.05 18:43

Dann jetzt nochmal für Morphi:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TForm1.Button1Click(Sender: TObject);
Var
 A : String;
 B : Char;
 Index : Integer;
 Ende : Integer;
 laenge : Integer;
begin
 A := Edit2.Text;
 laenge := Length(A); // Damit Length nur einmal aufgerufen werden muss
 Ende := (laenge div 2)+1;
 For Index := laenge downto ende do begin
   B := A[laenge+1-Index];
   A[laenge+1-Index] := A[Index];
   A[Index] := B;
 end;
 Edit1.Text := A;
end;


Dominique - Fr 25.02.05 18:45

Ist zwar nicht von mir, sondern von Ralph Friedmann (TeamB), aber bestimmt eine der schnellsten Methoden:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
function Reverse(S: string): string;
var
  I: integer;
  L: integer;
  P: PChar;
  Q: PChar;
begin
  L := Length(S);

  if L = 0 then
    Exit;

  SetLength(Result, L);

  P := PChar(Result);
  Q := PChar(S) + (L - 1);

  repeat
    P^ := Q^;
    Inc(P);
    Dec(Q);
  until Q = PChar(Pointer(S));
end;


und du nutzt es dann so:


Delphi-Quelltext
1:
Edit1.Text := Reverse(Edit2.Text);                    


Dominique - Fr 25.02.05 18:51
Titel: Re: String umdrehen
Sprint hat folgendes geschrieben:

Unter Delphi 7 gibt es die Funktion AnsiReverseString. Unter Delphi 5 nicht. Bei Delphi 6 weiß ich es nicht.


leider ist's mit stringfunktionen bis delphi 7 nicht so üppig.

ein tip an morphi: guck doch zwischendurch mal in die delphi hilfe :roll:


delfiphan - Fr 25.02.05 18:53

Ok, wir geben uns geschlagen :) Ist ca. 4 mal schneller als Jasocul's korrigierte Methode von uall ;)
// Edit: und ca. 20% schneller als meine, wenn man dort noch die Länge zuvor in einer Variablen speichert ;(


uall@ogc - Fr 25.02.05 19:13


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
procedure change(s: pchar);
asm
  push edx
  push ecx
  push ebx
  push esi
  xor edx, edx
@goon:
  mov cl, [eax+edx]
  inc edx
  test cl, cl
  jnz @goon
  dec edx
  dec edx
  mov esi, edx
  shr esi, 1

  xor ecx, ecx
@goon2:
  test esi, esi
  jz @ende
  mov bl, [eax+ecx]
  xchg byte ptr [eax+edx], bl
  mov byte ptr[eax+ecx], bl

  dec esi
  dec edx
  inc ecx
  jmp @goon2
@ende:
  pop esi
  pop ebx
  pop ecx
  pop edx
end;

procedure TForm1.FormCreate(Sender: TObject);
var s: string;
begin
  memo1.lines[0] := 'das ist ein test hahahahaha';
  s := memo1.lines[0];
  change(pchar(s));
  form1.caption := s;
end;


geht aber net bei hardcoded strings :)


jasocul - Fr 25.02.05 19:14

Das dürfte nicht so schnell zu toppen sein. *applaus*


opfer.der.genauigkeit - Fr 25.02.05 19:54

Sicherlich nicht getoppt, aber ich wollt auch was dazu beitragen.
*senfdazugeb* :wink:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
  edtReverseOutput.Text := res;

  sIn := edtReverseInput.Text;
  len := Length(sIn);

  SetLength(sOut, Len);

  for i := 1 to Len do begin
    pchar(integer(pointer(sOut)) + len - i)^ := pchar(integer(pointer(sIn)) + i - 1)^;
  end;


  edtReverseOutput.Text := sOut;


Moderiert von user profile iconraziel: Code- durch Delphi-Tags ersetzt.


delfiphan - Sa 26.02.05 00:52

Also die Assemblerfunktion funzt bei mir nicht ;)

Übrigens

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
  For I := 1 to length(S) do
  begin
    P^ := Q^;
    Inc(P);
    Dec(Q);
  end;

Wird übersetzt in (hier ist anfänglich eax = length(S)):

Quelltext
1:
2:
3:
4:
5:
6:
 mov dl,[esi]
 mov [ebx],dl
 inc ebx
 dec esi
 dec eax
 jnz -$09

Man hat zwar schlussendlich eine Kopie des Strings, aber performancemässig lässt es sich wohl nicht mehr toppen. Das ist vor allem schnell, weil die Indices nicht ständig neu errechnet werden müssen (im Stil von "length-i+1" oder sowas).


opfer.der.genauigkeit - Sa 26.02.05 04:20

@morphi

Bedien dich, es sind genügend Vorschläge da.
Ich glaube man erlebt nicht so oft, dass sich die Leute nen kleinen Kontestliefern um die
beste Lösung zu finden.. :lol:

//Edit: Achja :welcome:


IngoD7 - Sa 26.02.05 11:38
Titel: Re: String umdrehen
Ohne irgendwelche Geschwindigkeitsräusche mit ausleben zu wollen, plädiere ich auch für ein einfaches niedliches ReverseString.


delfiphan - Sa 26.02.05 12:05

@uall: Ich glaub Morphi hat Angst gekriegt wegen dem Assembler :x Jetzt kommt er nicht mehr zurück ;)


uall@ogc - Sa 26.02.05 12:32


Delphi-Quelltext
1:
2:
3:
4:
5:
@goon:   
  mov cl, [eax+edx]   // zeichen in cl
  inc edx               // edx hochzählen
  test cl, cl         // zeichen auf 0 testen
  jnz @goon             // wenn nicht wiederhole

das ist meine length funktion

wie gesgat das funzt nur bei strings wo man schreibzugrif drauf hat, bei den hard codes strings gehts net weil die in dem code drin stehen der aber nur READ_EXECUTE rechte hat, nen viretual protect solltedas lösen können
ansonsten erstellt delphi vorher immer eine kopie des strings


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
  dec edx           //
  dec edx           // länge um 2 kürzen wegen 1 mal zu viel hochgezählt und der #0
  mov esi, edx     // in ESI
  shr esi, 1     // länge um 2 dividieren, SHR schnellste funktion (besser als delphi mit SAR? des gelöst hat) 

  xor ecx, ecx     // ECX = 0
@goon2:    
  test esi, esi                        // ESI auf 0 testen   
  jz @ende                              // wenn ja zum ende
  mov bl, [eax+ecx]                  // in bl das erste zeichen
  xchg byte ptr [eax+edx], bl      // das letze zeichen mit BL austauschen  (schneller als das was delphi macht)
  mov byte ptr[eax+ecx], bl            // BL ans erste zeichen

  dec esi                              // ESI und EDX runter zählen und ECX hoch
  dec edx   
  inc ecx   
  jmp @goon2                        // und von vorne 
@ende:   
  pop esi   
  pop ebx   
  pop ecx   
  pop edx


delfiphan - Sa 26.02.05 12:52

Ok, deine Funktion mag unter allen Funktionen - die keine Kopien erstellen - die schnellste sein. Aber wer will denn schon die Reihenfolge eines 10mb Strings umdrehn? Sogar bei 10mb würd ich kurz ne Kopie erstellen. Kommt natürlich immer auf den einzelnen Fall an. Aber auch bei 10mb sehe ich Speed vor Speichergebrauch.
Die Schleife hier:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
For I := 1 to length(S) do  
begin  
  P^ := Q^;  
  Inc(P);  
  Dec(Q);  
end;

ist bedeutend schneller als das ganze Assembler Zeug.


uall@ogc - Sa 26.02.05 13:13

ok ich geb mich geschlagen, würde eh eine fertige funktion nehmen anstatt assembler zeugs ^^