Autor |
Beitrag |
xsus
      
Beiträge: 51
Win 2000, Win XP, Win 7; Ubuntu
Delphi 7, Delphi XE2, C
|
Verfasst: Di 06.08.13 23:19
Hey,
ich bin an einem kleinen Testprogramm am schreiben (soll später in ein größeres Projekt integriert werden). Zum jetzigen Zeitpunkt soll das Programm bei Eingabe ins Edit1-Feld (+Buttonclick) eine Reihe von ListItems durchgehen und vergleichen. Anschließend soll die Prozedur mit dem jeweiligen Namen (also der eingegebene) ausgeführt werden.
Testweise sieht das wiefolgt aus: Die Begriffe : Photosynthese, DNA und Adhäsion sollen eingegeben werden können und die jeweilige Prozedur markiert einen RadioButton...
Leider kommt es regelmäßig zu Stackoverflows und ich habe keine Ahnung wieso...bei Photosynthese und DNA als Begriff scheint es zu klappen...liegt es evtl. an Ä;Ü;Ö? Wie kann ich auch diese Buchstaben vergleichen lassen?
Wieso ändert sich der Fenstertitel und wie kann ich das verhindern?
//Die Eingabe von den 3 Begriffen (Photosynthese,..) habe ich manuell über die Eigenschaften in die ListBox gesetz, .., tauchen also nicht im Quellcode auf
Quellcode:
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: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TForm1 = class(TForm) Edit1: TEdit; RadioButton1: TRadioButton; RadioButton2: TRadioButton; RadioButton3: TRadioButton; Button1: TButton; ListBox1: TListBox; procedure Photosynthese; procedure DNA; procedure Adhaesion; procedure ItemsWeiter; procedure start; procedure Button1Click(Sender: TObject); private public end;
var Form1: TForm1; TEXT:String; Zahl:integer;
implementation
{$R *.dfm}
procedure TForm1.Photosynthese; begin RadioButton1.Checked:=true; end;
procedure TForm1.DNA; begin RadioButton2.Checked:=true; end;
procedure TForm1.Adhaesion; begin RadioButton3.Checked:=true; end;
function vergleicheStringsN(s1, s2:string; n:Word): Boolean; function delLZ(s: string): string; var ss: string; i,j: Word; begin j:=0; ss:=''; for i:=1 to Length(s) do begin if (s[i] <> ' ') and (s[i] <> '-') then begin Inc(j); ss:= ss + s[i] end; if j=n then Break end; Result := UpperCase(ss) end; begin if delLZ(s1)=delLZ(s2) then Result:=True else Result:=False ; end;
procedure TForm1.ItemsWeiter; begin if zahl=ListBox1.Items.Count -1 then begin end else begin Text := ListBox1.Items.Strings[zahl]; zahl:=zahl+1; end; end;
procedure TForm1.start; begin ItemsWeiter; if vergleicheStringsN(Edit1.Text, Text, 8)=True then case zahl of 1: Photosynthese; 2: DNA; 3: Adhaesion; end else start; end;
procedure TForm1.Button1Click(Sender: TObject); begin start; end; end. |
Z.Zt. steht mir leider nur Delphi 7 zur Verfügung..
Grüße und Dank von
xsus
|
|
Mathematiker
      
Beiträge: 2622
Erhaltene Danke: 1448
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: Di 06.08.13 23:33
Hallo,
xsus hat folgendes geschrieben : | Wieso ändert sich der Fenstertitel und wie kann ich das verhindern?
...
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| procedure TForm1.ItemsWeiter; begin if zahl=ListBox1.Items.Count -1 then begin end else begin Text := ListBox1.Items.Strings[zahl]; zahl:=zahl+1; end; end; | |
"Text" als globale Variable ist sehr ungünstig, da in dieser Methode die Variable Text = Form1.Text ist, d.h. also der Fenstertitel. Globale Variablen sind an sich nicht so prickelnd.
Hier würde ich einfach Umbenennen, z.B. "Suchtext" oder so, empfehlen.
Eine Idee für die anderen Problemen braucht noch etwas Zeit.
Beste Grüße
Mathematiker
Nachtrag: In
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| procedure TForm1.start; begin ItemsWeiter; if vergleicheStringsN(Edit1.Text, Text, 8)=True then case zahl of 1: Photosynthese; 2: DNA; 3: Adhaesion; end else start; end; |
rufst Du die Methode start rekursiv auf. Wenn ich das richtig sehe, wird diese damit nur dann beendet, wenn der letzte String der Listbox Deinem "Text" (Ändern!) entspricht.
Das führt in der Regel zum Stacküberlauf.
_________________ Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
Für diesen Beitrag haben gedankt: xsus
|
|
jasocul
      
Beiträge: 6393
Erhaltene Danke: 147
Windows 7 + Windows 10
Sydney Prof + CE
|
Verfasst: Mi 07.08.13 07:57
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| function vergleicheStringsN(s1, s2:string; n:Word): Boolean; function delLZ(s: string): string; var ss: string; i,j: Word; begin j:=0; ss:=''; for i:=1 to Length(s) do begin if (s[i] <> ' ') and (s[i] <> '-') then begin Inc(j); ss:= ss + s[i] end; if j=n then Break end; Result := AnsiUpperCase(ss) end; begin if delLZ(s1)=delLZ(s2) then Result:=True else Result:=False ; end; |
UpperCase berücksichtigt die (deutschen) Sondezeichen nicht. Daher musst du AnsiUpperCase verwenden.
Für diesen Beitrag haben gedankt: xsus
|
|
xsus 
      
Beiträge: 51
Win 2000, Win XP, Win 7; Ubuntu
Delphi 7, Delphi XE2, C
|
Verfasst: Mi 07.08.13 13:01
Mathematiker hat folgendes geschrieben : | Hallo,
"Text" als globale Variable ist sehr ungünstig, da in dieser Methode die Variable Text = Form1.Text ist, d.h. also der Fenstertitel. Globale Variablen sind an sich nicht so prickelnd.
Hier würde ich einfach Umbenennen, z.B. "Suchtext" oder so, empfehlen.
Eine Idee für die anderen Problemen braucht noch etwas Zeit. |
Ja, das hat Super geklappt! Danke
Zu dem Anderem - sowohl Uppercase als auch AnsiUpperCase machen keinen unterschied. :/
Wenn ich das Recht sehe, muss die ItemsWeiter und die Start-Procedure verändert werden. Ich habe jetzt div. Versucht, aber ohne Erfolg. Im Prinzip soll ja nur bei jeder Suchanfrage die ItemsListe durchgegangen werden und jedes Item jeweils verglichen werden. Anschließend die Nummer übergeben um die entsprechende Procedure auszuführen. Keiner eine Idee?
Beste Grüße!
|
|
Mathematiker
      
Beiträge: 2622
Erhaltene Danke: 1448
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: Mi 07.08.13 13:36
Hallo,
so ähnlich dürfte es funktionieren:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| procedure TForm1.start; var i:integer; s:string; begin i:=0; repeat s:=listbox1.items[i]; if vergleicheStringsN(Edit1.Text, s, 8)=True then case i+1 of 1: Photosynthese; ... end inc(i); until i>listbox1.items.count-1; end; |
Du benötigst keine globale Variable und keinen rekursiven Aufruf mehr.
Beste Grüße
Mathematiker
_________________ Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
Für diesen Beitrag haben gedankt: xsus
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Mi 07.08.13 13:38
Gelöscht,
ich habe in der Eile nicht erkannt, dass Mathematiker schon zuvor gepostet hat, welches auch eine bessere Version ist.
Gruß Horst
P.S.
Wie wird man den Dank wieder los?
Zuletzt bearbeitet von Horst_H am Do 08.08.13 07:19, insgesamt 1-mal bearbeitet
Für diesen Beitrag haben gedankt: xsus
|
|
FinnO
      
Beiträge: 1331
Erhaltene Danke: 123
Mac OSX, Arch
TypeScript (Webstorm), Kotlin, Clojure (IDEA), Golang (VSCode)
|
Verfasst: Mi 07.08.13 17:21
Bitte keine Vergleiche auf True durchführen.
Besser:
Delphi-Quelltext 1: 2:
| if vergleicheStringsN(Edit1.Text, Text, 8) then ... |
|
|
WasWeißDennIch
      
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: Do 08.08.13 12:18
Auch wenn ich mich unbeliebt machen sollte: ein paar Dinge stören mich an der Lösung. Zum Einen knallt es aufgrund der Fußschleife, sobald die ListBox keine Einträge enthält, da ungeprüft auf den String an Index 0 zugegriffen wird. Zum Anderen ist das Ganze durch die Vermischung von Logik und Darstellung unflexibel, man will doch eigentlich keine VCL-Controls vergleichen, sondern deren Inhalt, also Strings. Schöner fände ich persönlich es daher, wenn man eine allgemeinverwendbare Lösung anstreben würde. So etwas könnte z.B. so aussehen:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| type TMatchFunc = function(String1, String2: string): Boolean;
function SpecialIndexOf(SearchString: string; List: TStrings; MatchFunc: TMatchFunc): integer; var idx: integer; begin Assert(Assigned(List), 'Vergleichsliste nicht übergeben'); Assert(Assigned(MatchFunc), 'Vergleichsroutine nicht übergeben'); Result := -1; for idx := 0 to List.Count - 1 do if MatchFunc(SearchString, List[idx]) then begin Result := idx; break; end; end; |
Dadurch, dass man die Vergleichsroutine selbst schreiben muss, ist man in eben dieser völlig frei und kann mit den übergebenen Strings anstellen, was man will/muss. 2 Beispiele dazu:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| function MatchFunc1(String1, String2: string): Boolean; begin Result := AnsiLowerCase(String1) = AnsiLowerCase(String2); end;
procedure TFormTest.Button1Click(Sender: TObject); var List: TStringlist; begin List := TStringList.Create; try List.Add('Dies'); List.Add('ist'); List.Add('ein'); List.Add('Test'); ShowMessage(IntToStr(SpecialIndexOf('EIN', List, MatchFunc1))); finally List.Free; end; end; |
Das war jetzt noch keine große Kunst, daher hier ein wenig komplexer:
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:
| function MatchFunc2(String1, String2: string): Boolean;
function FilterNumbers(const s: string): string; var Src, Dest, Current: PChar; begin SetLength(Result, Length(s)); Src := PChar(s); Dest := PChar(Result); Current := Dest; while Src^ <> #0 do begin if Src^ in ['0'..'9'] then begin Current^ := Src^; Current := CharNext(Current); end; Src := CharNext(Src); end; SetString(Result, Dest, Current - Dest); end;
begin Result := FilterNumbers(String1) = FilterNumbers(String2); end;
procedure TFormTest.Button2Click(Sender: TObject); var List: TStringlist; begin List := TStringList.Create; try List.Add('Dies'); List.Add('ist123'); List.Add('ein'); List.Add('Test'); ShowMessage(IntToStr(SpecialIndexOf('Didel123dum', List, MatchFunc2))); finally List.Free; end; end; |
Man ist bei Verwendung von SpecialIndexOf an keine VCL-Controls mehr gebunden und kann die Vergleichsroutine (den Callback) völlig frei gestalten. Evtl. ist noch der eine oder andere Fehler enthalten, mir ging es aber vorrangig um das Prinzip.
Für diesen Beitrag haben gedankt: FinnO, Marc.
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Do 08.08.13 14:12
Hallo,
xsus ist ja noch Schüler, da kann man noch etwas bewirken, entschuldige...
wie bekommt man das hin
Delphi-Quelltext 1:
| Assert(Assigned(MatchFunc), 'Vergleichsroutine nicht übergeben'); |
ohne das der Compiler nicht hier schon vorher meckert?
Delphi-Quelltext 1:
| showMessage(IntToStr(SpecialIndexOf('EIN', List, MatchFunc1))); |
Oder stellst Du Dir die Verwendung eines array of TMatchfunc vor, indem dann NIL steht
Delphi-Quelltext 1:
| showMessage(IntToStr(SpecialIndexOf('EIN', List, MatchFuncArr[1]))); |
Ein großer Vorteil bei der Verwendung einer seperaten Liste besteht in der Möglichkeit, die Daten schon dort in das gewünschte Format zu bringen, ohne sie ständig umwandeln zu müssen.
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:
| function delLZ(s: string,MaxLen : word): string; var i,j: Word; begin j:=length(s);; IF maxLen > j then maxLen := j; j := 1; setlength(result,maxLen); for i:=1 to Length(s) do begin if (s[i] <> ' ') and (s[i] <> '-') then begin result[j] := s[i]; Inc(j) end; if j>MaxLen then Break end; setlength(result,j-1); Result := AnsiUpperCase(result) end; ...
procedure TFormTest.Button1Click(Sender: TObject); var List: TStringlist; i : integer; begin Assert(Assigned(ListBox1), 'Keine ListBox1 vorhanden'); i := ListBox1.Items.Count; IF i = 0 then Assert(false, 'Nichts vorhanden'); List := TStringList.Create; try List.capacity := i; For i := i-1 downto 0 do List[i] := delLZ(ListBox1.Items.Strings[i], 8); ShowMessage(IntToStr(SpecialIndexOf(delLZ('EIN'), List, MatchFunc1))); finally List.Free; end; end; |
Gruß Horst
|
|
WasWeißDennIch
      
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: Do 08.08.13 17:05
|
|
|