| Autor |
Beitrag |
eherzel
      
Beiträge: 22
XP
D5 Enterp
|
Verfasst: Fr 16.07.04 21:06
Hallo!
ich brauche eine Funktion, welche mir den Grad der Übereinstimmung zwischen 2 Strings liefert, z.B.:
str1 'Hallo Welt' str2 'Hallo Welt' --> 1.0 die Strings sind zu 100% identisch.
str1 'Hallo Welt' str2 'Halla Welt' --> 0.9 die Strings sind zu 90% identisch.
Enrico
|
|
MSCH
      
Beiträge: 1448
Erhaltene Danke: 3
W7 64
XE2, SQL, DevExpress, DevArt, Oracle, SQLServer
|
Verfasst: Sa 17.07.04 10:26
kram...such...find...!
Was du brauchst ist Fuzzy:
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:
| function Tdbl.compareMyStrings(_S1, _S2: String): integer; var hit: Integer; p1, p2: Integer; l1, l2: Integer; pt: Integer; diff: Integer; hstr: string; test: array [1..255] of Boolean; S1,S2: String; begin S1:= UpperCase(trim(_S1)); S2:= UpperCase(trim(_S2)); if (length(S1)=0 )or(length(S2)=0) then begin result:=100; exit; end; if Length(s1) < Length(s2) then begin hstr:= s2; s2:= s1; s1:= hstr; end; l1:= Length (s1); l2:= Length (s2); p1:= 1; p2:= 1; hit:= 0; diff:= Max (l1, l2) div 3 + ABS (l1 - l2); for pt:= 1 to l1 do test[pt]:= False; repeat if not test[p1] then begin if (s1[p1] = s2[p2]) and (ABS(p1-p2) <= diff) then begin test[p1]:= True; Inc (hit); Inc (p1); Inc (p2); if p1 > l1 then p1:= 1; end else begin test[p1]:= False; Inc (p1); if p1 > l1 then begin while (p1 > 1) and not (test[p1]) do Dec (p1); Inc (p2) end; end; end else begin Inc (p1); if p1 > l1 then begin repeat Dec (p1); until (p1 = 1) or test[p1]; Inc (p2); end; end; until p2 > Length(s2); try Result:= 100 * hit DIV l1; except result:=100; end; end; |
viel spass
grez
msch
_________________ ist das politisch, wenn ich linksdrehenden Joghurt haben möchte?
|
|
eherzel 
      
Beiträge: 22
XP
D5 Enterp
|
Verfasst: Sa 17.07.04 14:05
In Delphi5 bekomme ich beim kompilieren eine Fehlermeldung:
[Error] MainForm.pas(57): Undeclared identifier: 'Max'
Das Ganze bezieht sich auf die Zeile:
Quelltext 1:
| diff:= Max (l1, l2) div 3 + ABS (l1 - l2); |
Kann es sein, daß diese Funktion in Delphi5 noch nicht zur Verfügung steht?
|
|
eherzel 
      
Beiträge: 22
XP
D5 Enterp
|
Verfasst: Sa 17.07.04 14:14
Hab es gefunden, man muß auch die Unit Math mit einbinden.
Routine läuft soweit ganz gut, recht vielen Dank!!!!!!!!!!!!
|
|
eherzel 
      
Beiträge: 22
XP
D5 Enterp
|
Verfasst: Sa 17.07.04 14:36
etwas seltsam erscheint mir jedoch folgendes Ergebnis:
Str1: "der lange Text" Str2: "den lange Text" liefert als Ergebnis 71
aber Str1: "der langer Text" Str2: "den langer Text" liefert als Ergebnis 20
Obwohl da jeweils an identischer Stelle ein identischer Buchstabe hinzukam, sinkt der Grad der Übereinstimmung von 71 auf 20, kannst du mir das evtl. erklären?
|
|
MSCH
      
Beiträge: 1448
Erhaltene Danke: 3
W7 64
XE2, SQL, DevExpress, DevArt, Oracle, SQLServer
|
Verfasst: Sa 17.07.04 17:18
probier mal ein bischen mit DIFF -der Unschärfefaktor.
grübel, mal sehen, wo ich das teil her hab...
grez
msch
_________________ ist das politisch, wenn ich linksdrehenden Joghurt haben möchte?
|
|
Dr. Phil
      
Beiträge: 66
Win XP
Delphi 7 Prof.
|
Verfasst: Sa 17.07.04 20:59
Versuch mal das: 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:
| function CompareStrings(S1: string; S2: string): Real; var LenA, LenB, i, fAlphas: Integer; h: string; begin fAlphas := 0; LenA := Length(S1); LenB := Length(S2); If ((LenA = 0) and (LenB > 0)) or ((LenB = 0) and (LenA > 0)) Then begin CompareStrings := 0; exit; end Else If (LenA = 0) and (LenB = 0) Then begin CompareStrings := 1; exit; end; If LenB > LenA Then begin h := S1; S1 := S2; S2 := h; i := LenA; LenA := LenB; LenB := i; end; For i := 1 to LenA do If S1[i] <> S2[i] Then Inc(fAlphas); If fAlphas > LenA Then CompareStrings := 0 else CompareStrings := LenA / (LenA + fAlphas); end; |
Ist zwar ein bisschen ungenau, hatte nur 5 min Zeit,
aber es sollte reichen. ('1' stimmt mit '12' zu 0,66667 überein)
_________________ self-improvement is masturbation;
self-destruction is the answer - Tyler Durden
|
|
eherzel 
      
Beiträge: 22
XP
D5 Enterp
|
Verfasst: Sa 17.07.04 21:10
sieht schon sehr gut aus, mal abgesehen von case sensitiv, welches man optional einbauen kann und sollte und eine Überprüfung der Längen der beiden Strings, sonst gibt es bei folgender Zeile ein Exception, wenn beide Strings unterschiedlich lang sind:
If S1[i] <> S2[i] Then Inc(fAlphas);
Ansonsten läuft es schon ganz gut!!!!
|
|
Dr. Phil
      
Beiträge: 66
Win XP
Delphi 7 Prof.
|
Verfasst: Sa 17.07.04 21:28
Also bei mir kommt keine Exception.
Man könnte natürlich den kürzeren String mit so vielen Leerzeichen füllen, bis er genausolang ist wie der längere String.
Case sensitiv kannst du einbauen, indem du die Überprüfung am Schluss mit ord() machst: Delphi-Quelltext 1:
| If ord(S1[i]) <> ord(S2[i]) Then Inc(fAlphas); |
Zumindest sollte es so gehen, da dann der ASCII-Wert der Chars verglichen wird. Nun wird auch Case sensitiv unterschieden.
_________________ self-improvement is masturbation;
self-destruction is the answer - Tyler Durden
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Sa 17.07.04 21:40
| Dr. Phil hat folgendes geschrieben: | | Case sensitiv kannst du einbauen, indem du die Überprüfung am Schluss mit ord() machst:If ord(S1[i]) <> ord(S2[i]) Then Inc(fAlphas); |
Case Sensitive macht schon die Ausgangsfunktion. Ich denk mal eher Case Insensitive war gemeint:
Delphi-Quelltext 1:
| If LowerCase(S1[i]) <> LowerCase(S2[i]) Then Inc(fAlphas); |
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
Dr. Phil
      
Beiträge: 66
Win XP
Delphi 7 Prof.
|
Verfasst: Sa 17.07.04 21:55
Stimmt, hab ich verwechselt 
_________________ self-improvement is masturbation;
self-destruction is the answer - Tyler Durden
|
|
eherzel 
      
Beiträge: 22
XP
D5 Enterp
|
Verfasst: So 18.07.04 11:19
Die Exception lautet 'Range Check Error', was ja auch klar ist, weil du in der betreffenden Zeile mit S1[i] bzw. S2[i] auf eine Position im String zugreifst, welche nicht vorhanden ist durch die unterschiedlichen Längen. Was wäre dann in diesem Falle der günstigere Weg im Hinblick auf das Endergebnis, das ganze via try abfangen oder den jeweils kürzeren String durch Blanks auffüllen?
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: So 18.07.04 18:52
Ben kürzeren String mit #0-Zeichen auffüllen.
Theoretisch müsste auch ein SetLength(ShorterString, Length(LongerString)); funktionieren.
Ansonsten müsste auch ein {$R-} funktionieren, sollte aber vermieden werden, da dies zu unerwarteten Ergebnissen führen kann.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
Dr. Phil
      
Beiträge: 66
Win XP
Delphi 7 Prof.
|
Verfasst: Mo 19.07.04 07:27
Ab da: Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| If LenB > LenA Then begin h := S1; S1 := S2; S2 := h; i := LenA; LenA := LenB; LenB := i; end; |
ist der kürzere String immer S2.
Bei mir kommt beim Aufrufen keine Exception, liegt wohl an den unterschiedlichen Delphi-Versionen.
Mit #0 auffüllen ist schon mal eine gute Lösung.
So funktioniert es bei mir: Delphi-Quelltext 1: 2: 3: 4:
| For i := 1 to LenB do begin If S2[i] = '' Then S2[i] := ' '; end; |
* Statt dem Leerzeichen könnte man #0 einsetzen, vorausgesetzt, dass das keinen Fehler ergibt, da #0 ja das Ende des Strings markiert.
Hab es noch nicht mit #0 ausprobiert, mit Leerzeichen funkitionierts aber schon ganz gut.
Leerzeichen sind halt deshalb nicht so gut geeignet, weil es leicht passieren kann dass dann mal 2 Leerzeichen übereinstimmen, und der Wert ist dann höher als er sein sollte.
_________________ self-improvement is masturbation;
self-destruction is the answer - Tyler Durden
|
|
eherzel 
      
Beiträge: 22
XP
D5 Enterp
|
Verfasst: Mo 19.07.04 07:44
also ich habe es mal mit dem
Quelltext 1:
| SetLength(ShorterString, Length(LongerString)); |
in diesem Falle also
Quelltext 1:
| SetLength(S2, Length(S1)); |
probiert und es kommt jetzt kein Exception mehr, was immer auch diese Funktion SetLength macht.
|
|
Dr. Phil
      
Beiträge: 66
Win XP
Delphi 7 Prof.
|
Verfasst: Mo 19.07.04 07:57
Naja SetLength macht den String S2 (also den Kürzeren) jetzt so lang wie S1 (den Längeren).
Wird wahrscheinlich auch nichts anderes machen als #0 dazzuschreiben
Na Hauptsache es funktioniert so.
_________________ self-improvement is masturbation;
self-destruction is the answer - Tyler Durden
|
|
eherzel 
      
Beiträge: 22
XP
D5 Enterp
|
Verfasst: Mo 19.07.04 11:09
RTFM
hab mal nachgeschaut, SetLength erweitert nur den Platz des Strings, wobei dann irgendwelche Werte in diesem Bereich stehen, also ist dies auch nicht die optimale Lösung, ebensowenig, das ganze mit Leerzeichen aufzufüllen, weil eben, wie du schon anmerktest, es da auch zu zufälligen Übereinstimmungen kommen kann, welche das Ergebnis verfälschen. Für mein momentanes Projekt wäre es aber erst einmal ausreichend, vielleicht fällt einem ja für die Zukunft noch ein sinnvoller Weg ein, wie man diesen Fall am besten lösen kann, zum Beispiel durch setzen von Negativwerten für jedes fehlende zeichen oder so.
|
|
Eggi
      
Beiträge: 33
Win 2000, Win XP
D7 Prof
|
Verfasst: Mo 19.07.04 13:09
Um Wortähnlichkeiten festzustellen nutze ich den Levenshtein-Algorithmus. Mithilfe dieses Algorithmus läßt sich ermitteln, wieviele Einfüge-, Lösch- oder Austauschoperationen nötig sind, um eine Zeichenkette in eine andere zu überführen. Dabei wird jede Operation mit Strafpunkten belegt. Also je größer der Wert ist, den die Funktion ermittelt desto unterschiedlicher sind die beiden Zeichenkette.
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:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private public end;
var Form1: TForm1; FiR0 : integer; FiP0 : integer; FiQ0 : integer;
implementation
{$R *.DFM}
function Min(X,Y,Z: Integer): Integer; begin if (X<Y) then Result:=X else Result:=Y; if (Result>Z) then Result:=Z; end;
procedure LevenshteinPQR(p,q,r:integer); begin FiP0 := p; FiQ0 := q; FiR0 := r; end;
function LevenshteinDistance(const sString,sPattern: String): Integer; const MAX_SIZE = 50; var aiDistance: array [0..MAX_SIZE,0..MAX_SIZE] of Integer; i,j,iStringLength,iPatternLength,iMaxI,iMaxJ: Integer; chChar: Char; iP,iQ,iR,iPP: Integer; begin iStringLength:=length(sString); if (iStringLength>MAX_SIZE) then iMaxI:=MAX_SIZE else iMaxI:=iStringLength; iPatternLength:=length(sPattern); if (iPatternLength>MAX_SIZE) then iMaxJ:=MAX_SIZE else iMaxJ:=iPatternLength;
aiDistance[0,0]:=0; for i:=1 to iMaxI do aiDistance[i,0]:=aiDistance[i-1,0]+FiR0;
for j:=1 to iMaxJ do begin chChar:=sPattern[j]; if ((chChar='*') or (chChar='?')) then iP:=0 else iP:=FiP0; if (chChar='*') then iQ:=0 else iQ:=FiQ0; if (chChar='*') then iR:=0 else iR:=FiR0;
aiDistance[0,j]:=aiDistance[0,j-1]+iQ;
for i:=1 to iMaxI do begin if (sString[i]=sPattern[j]) then iPP:=0 else iPP:=iP; aiDistance[i,j]:=Min(aiDistance[i-1,j-1]+iPP, aiDistance[i,j-1] +iQ , aiDistance[i-1,j] +iR); end; end; Result:=aiDistance[iMaxI,iMaxJ]; end;
procedure TForm1.Button1Click(Sender: TObject); begin LevenshteinPQR(1,2,2); showmessage(inttostr(LevenshteinDistance('falsch','f*lsch'))); showmessage(inttostr(LevenshteinDistance('falsch','falach'))); showmessage(inttostr(LevenshteinDistance('falsch','fasch'))); end;
end. |
|
|
eherzel 
      
Beiträge: 22
XP
D5 Enterp
|
Verfasst: Mo 19.07.04 15:29
könntest du mir evtl. noch sagen, welcher der drei Werte für was zuständig ist, damit ich die einzelnen Operationen entsprechend meinen Vorstellungen gewichten kann?
|
|
Eggi
      
Beiträge: 33
Win 2000, Win XP
D7 Prof
|
Verfasst: Di 20.07.04 09:41
Mit den drei Werten beeinflußt man die Gewichtung der drei Operationen Weglassen, Hinzufügen und Ändern.
|
|
|