



Para um correto entendimento sobre o problema é recomendável ler sobre
longest common subsequence (LCS) problem na Wikipedia.
Depois de ler o artigo da Wikipedia sobre, Yanniel obteve uma visão geral do problema e decidiu implementar uma classe Delphi para fazer o truque de comparação de strings, que é a base para a comparação de arquivos de texto.
No exemplo temos duas strings para serem comparadas, onde vamos destacar em azul os caracteres adicionados à primeira string e em vermelho os caracteres removidos dela. Os caracteres comuns (inalterados) manterão a cor padrão.
A classe de comparação irá parecer com isso:
1 2 3 4 5 6 7 8 9 10 11 | type TDiff = record Character: Char; CharStatus: Char; //Possible values: [+, -, =] end; TStringComparer = class …………… public class function Compare(aString1, aString2: string): TList<TDiff>; end; |
Quando você chama TStringComparer.Compare, uma lista genérica de registros TDiff é criada. Um registro TDiff contém um caractere e se esse caractere foi adicionado ( CharStatus = ‘+’), removido ( CharStatus = ‘-‘) ou inalterado ( CharStatus = ‘=’) em ambas as sequências sob comparação.
Vamos colocar duas edits ( Edit1 , Edit2 ), uma edição rica ( RichEdit1 ) e um botão ( Button1 ) em um formulário Delphi. Para destacar as diferenças, coloque o seguinte código no evento OnClick do botão:
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 | procedure TForm1.Button1Click(Sender: TObject); var Differences: TList<TDiff>; Diff: TDiff; begin //Yes, I know...this method could be refactored ;-) Differences:= TStringComparer.Compare(Edit1.Text, Edit2.Text); try RichEdit1.Clear; RichEdit1.SelStart:= RichEdit1.GetTextLen; for Diff in Differences do if Diff.CharStatus = '+' then begin RichEdit1.SelAttributes.Color:= clBlue; RichEdit1.SelText := Diff.Character; end else if Diff.CharStatus = '-' then begin RichEdit1.SelAttributes.Color:= clRed; RichEdit1.SelText:= Diff.Character; end else begin RichEdit1.SelAttributes.Color:= clDefault; RichEdit1.SelText:= Diff.Character; end; finally Differences.Free; end; end; |
O programa irá fazer parecido com a imagem abaixo:
Abaixo segue um exemplo de implementação, lógico que diversas melhorias podem ser implementadas.
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 101 102 103 | unit StringComparison; interface uses Math, Generics.Collections; type TDiff = record Character: Char; CharStatus: Char; //Possible values: [+, -, =] end; TStringComparer = class strict private type TIntArray = array of array of Integer; class function LCSMatrix(X, Y: string): TIntArray; class procedure ComputeDifferences(aLCSMatrix: TIntArray; X, Y: string; i, j: Integer; aDifferences: TList<TDiff>); public class function Compare(aString1, aString2: string): TList<TDiff>; end; implementation { TStringComparer } class function TStringComparer.LCSMatrix(X, Y: string): TIntArray; var m, n, i, j: Integer; begin m:= Length(X); n:= Length(Y); //We need one extra column and one extra row to be filled with zeroes SetLength(Result, m + 1, n + 1); //First column filled with zeros for i := 0 to m do Result[i, 0] := 0; //First row filled with zeros for j:= 0 to n do Result[0, j]:= 0; //Storing the lengths of the longest common subsequences //between prefixes of X and Y for i:= 1 to m do for j:= 1 to n do if X[i] = Y[j] then Result[i, j] := Result[i-1, j-1] + 1 else Result[i, j]:= Max(Result[i, j-1], Result[i-1, j]); end; class procedure TStringComparer.ComputeDifferences(aLCSMatrix: TIntArray; X, Y: string; i, j: Integer; aDifferences: TList<TDiff>); var CharDiff: TDiff; begin if (i > 0) and (j > 0) and (X[i] = Y[j]) then begin ComputeDifferences(aLCSMatrix, X, Y, i-1, j-1, aDifferences); CharDiff.Character:= X[i]; CharDiff.CharStatus:= '='; //The character did not change aDifferences.Add(CharDiff); end else if (j > 0) and ((i = 0) or (aLCSMatrix[i,j-1] >= aLCSMatrix[i-1,j])) then begin ComputeDifferences(aLCSMatrix, X, Y, i, j-1, aDifferences); CharDiff.Character:= Y[j]; CharDiff.CharStatus:= '+'; //The character was added aDifferences.Add(CharDiff); end else if (i > 0) and ((j = 0) or (aLCSMatrix[i,j-1] < aLCSMatrix[i-1,j])) then begin ComputeDifferences(aLCSMatrix, X, Y, i-1, j, aDifferences); CharDiff.Character:= X[i]; CharDiff.CharStatus:= '-'; //The character was removed aDifferences.Add(CharDiff); end; end; //This is a factory method class function TStringComparer.Compare(aString1, aString2: string): TList<TDiff>; var Matrix: TIntArray; begin Result:= TList<TDiff>.Create; Matrix:= LCSMatrix(aString1, aString2); ComputeDifferences(Matrix, aString1, aString2, Length(aString1), Length(aString2), Result); end; end. |
Baixe aqui os fontes do exemplo do post
[download id=”3107″]
Dúvidas ou sugestões? Deixe o seu comentário!
Baseado no post: http://www.yanniel.info/2012/01/lcs-string-comparison-in-delphi.html