Autor Beitrag
eherzel
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22

XP
D5 Enterp
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1448
Erhaltene Danke: 3

W7 64
XE2, SQL, DevExpress, DevArt, Oracle, SQLServer
BeitragVerfasst: Sa 17.07.04 10:26 
kram...such...find...!
Was du brauchst ist Fuzzy:
ausblenden volle Höhe 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:
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; // Number of identical chars
    p1, p2: Integer; // Position count
    l1, l2: Integer; // Length of strings
    pt: Integer; // for counter
    diff: Integer; // unsharp factor
    hstr: string// help var for swapping strings
    // Array shows is position is already tested
    test: array [1..255of Boolean;
    S1,S2: String;
begin
 S1:= UpperCase(trim(_S1)); // normalize
 S2:= UpperCase(trim(_S2)); // dito
 if (length(S1)=0 )or(length(S2)=0then begin
   result:=100;
   exit;
 end;
 // Test Length and swap, if s1 is smaller
 // we alway search along the longer string
 if Length(s1) < Length(s2) then begin
  hstr:= s2; s2:= s1; s1:= hstr;
 end;
 // store length of strings to speed up the function
 l1:= Length (s1);
 l2:= Length (s2);
 p1:= 1; p2:= 1; hit:= 0;
 // calc the unsharp factor depending on the length
 // of the strings. Its about a third of the length
 diff:= Max (l1, l2) div 3 + ABS (l1 - l2);
 // init the test array
 for pt:= 1 to l1 do test[pt]:= False;
 // loop through the string
 repeat
  // position tested?
  if not test[p1] then begin
   // found a matching character?
   if (s1[p1] = s2[p2]) and (ABS(p1-p2) <= diff) then begin
    test[p1]:= True;
    Inc (hit); // increment the hit count
    // next positions
    Inc (p1); Inc (p2);
    if p1 > l1 then p1:= 1;
   end else begin
    // Set test array
    test[p1]:= False;
    Inc (p1);
    // Loop back to next test position if end of the string
    if p1 > l1 then begin
     while (p1 > 1and not (test[p1]) do Dec (p1);
     Inc (p2)
    end;
   end;
  end else begin
   Inc (p1);
   // Loop back to next test position if end of string
   if p1 > l1 then begin
    repeat Dec (p1); until (p1 = 1or test[p1];
    Inc (p2);
   end;
  end;
 until p2 > Length(s2);
 // calc procentual value
 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22

XP
D5 Enterp
BeitragVerfasst: 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:

ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22

XP
D5 Enterp
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22

XP
D5 Enterp
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1448
Erhaltene Danke: 3

W7 64
XE2, SQL, DevExpress, DevArt, Oracle, SQLServer
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 66

Win XP
Delphi 7 Prof.
BeitragVerfasst: Sa 17.07.04 20:59 
Versuch mal das:
ausblenden volle Höhe 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:
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 = 0and (LenB > 0)) or ((LenB = 0and (LenA > 0)) Then
  begin
    CompareStrings := 0;
    exit;
  end
  Else
    If (LenA = 0and (LenB = 0Then
    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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22

XP
D5 Enterp
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 66

Win XP
Delphi 7 Prof.
BeitragVerfasst: 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:
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: 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:
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 66

Win XP
Delphi 7 Prof.
BeitragVerfasst: Sa 17.07.04 21:55 
Stimmt, hab ich verwechselt :roll:

_________________
self-improvement is masturbation;
self-destruction is the answer - Tyler Durden
eherzel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22

XP
D5 Enterp
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 66

Win XP
Delphi 7 Prof.
BeitragVerfasst: Mo 19.07.04 07:27 
Ab da:
ausblenden 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:
ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22

XP
D5 Enterp
BeitragVerfasst: Mo 19.07.04 07:44 
also ich habe es mal mit dem
ausblenden Quelltext
1:
SetLength(ShorterString, Length(LongerString));					

in diesem Falle also
ausblenden Quelltext
1:
SetLength(S2, Length(S1));					

probiert und es kommt jetzt kein Exception mehr, was immer auch diese Funktion SetLength macht.
Dr. Phil
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 66

Win XP
Delphi 7 Prof.
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22

XP
D5 Enterp
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 33

Win 2000, Win XP
D7 Prof
BeitragVerfasst: 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.

ausblenden volle Höhe 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:
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
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  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] := Minimum of 3 values ***}
   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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22

XP
D5 Enterp
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 33

Win 2000, Win XP
D7 Prof
BeitragVerfasst: Di 20.07.04 09:41 
Mit den drei Werten beeinflußt man die Gewichtung der drei Operationen Weglassen, Hinzufügen und Ändern.