Feedback

C# - Prozentual Textvergleichen

Veröffentlicht von am 4/12/2007
(1 Bewertungen)
Ein Textvergleicher, mit dem es möglich ist, zwei Zeichenfolge auf Gleichheit zu prüfen. Die Übereinstimmung wird in Prozent zurückgegeben.
using System;
using System.Collections.Generic;
using System.Text;

namespace MySystem.Text
{
	/// <summary>
	/// Ein Textvergleicher, der als Rückgabe die prozentuale Übereinstimmung zurück gibt. 
	/// Es können dabei mehrere verschiedene Einstellungen vorgenommen werden.
	/// Alle Prozentangaben, egal ob Rückgabe oder Festlegen, 100 Prozent = 1
	/// 
	/// Beim Vergleichen wird ganz normal von vorne angefangen. Jede Übereinstimmung ist 100 Prozent.
	/// Diese werden beim Debuggen in der Matrix gespeichert und anschließend zusammengezählt und die Prozente
	/// dann von ersten Zeichenfolgen (die Länge) berechnet.
	/// 
	/// Es gibt für beide Zeichenfolgen zwei seperate Indexe, die beide seperat hochgezählt werden, bzw. der zweite Index auch springen kann,
	/// wenn ein Zeichen an der falschen Stelle ist.
	/// 
	/// Wenn ein Zeichen nicht vorhanden ist, wird als nächstes gesucht, wo sich das zusuchende Zeichen als nächstes befinden.
	/// Dabei wird die aktuelle Position als Ausgangspunkt genommen. Das näher gelegene Zeichen hat gewonnen und wird als neuer Index für
	/// die 2. Zeichenfolge genommen. Die Prozente für die aktuelle Position berechnen sich aus dem Abstand der aktuellen Position, mit dem 
	/// gefundenem Zeichen, welches am nächsten liegt. Als Basislänge, wird wieder die Länge von der ersten Zeichenkette verwendet. Dies Lässt sich
	/// aber mit den 2 Eigenschaften zum Sortieren ändern.
	/// 
	/// Wenn nach einander mehrere Zeichen nicht an der richtigen Position sind, wird ein extra Zähler hochgezählt,
	/// der die Anzahl der falschen Zeichen misst und dann von der bereits errechneten Übereinstimmung nochmal ein Prozentsatz abgezogen.
	/// Wenn PutBack auf true ist, wird wenn ein Zeichen wieder an der richtigen Position ist, der bereits falschen Zeichen zurück auf 0 gesetzt.
	/// 
	/// Mit MaxAway lässt sich einstellen, weit ein Zeichen von der aktuellen Position entfernt sein darf, bzw. wieviel Prozent für ein zeichen
	/// mind. erreicht werden müssen. Ist dies kleiner, hat die aktuelle Position 0 Prozent.
	/// 
	/// Die neue aktuelle Position für die zweite Zeichenfolge ist dann das gerade eben gefundene Zeichen. Wurde keins gefunden, bleibt die
	/// Position unverändert.
	/// </summary>
	public class MyStringComparer
	{
		#region Felder

		/// <summary>
		/// Unterscheidung zwischen Groß und Kleinschreibung
		/// </summary>
		private bool _ignoreCase = true;

		/// <summary>
		/// Der Abstand in Prozent, die ein zu suchendes Zeichen maximal von der aktuellen Position entfernt sein darf.
		/// </summary>
		private float _maxAway = 0.90F;

		/// <summary>
		/// Wenn true, werden die zwei übergebenen Strings bei Bedarf getauscht, dass die Längere Zeichenfolge als erstes Argument und die kürzere als zweites Argument.
		/// </summary>
		private bool _sortVars = false;

		/// <summary>
		/// Wenn true und wenn _sortVars true, dann wird beim Tauschen der Zeichenfolgen die Längere Zeichenfolge zuerst genommen.
		/// </summary>
		private bool _LongerVarFirst = false;

		/// <summary>
		/// Wenn auf true gesetzt wurde, wird nach dem Fehler aufgetreten sind und das aktuelle Zeichen wieder stimmt, die Fehleranzahl zurückgesetzt.
		/// </summary>
		private bool _putBack = true;

		/// <summary>
		/// Entfernt überschüssige Leerzeichen.
		/// </summary>
		private bool _removeDoubleWhitespaces = true;

#if DEBUG
		/// <summary>
		/// 
		/// </summary>
		private float[] _lastmatrix;
#endif

		#endregion

		#region Eigenschaften

#if DEBUG

		/// <summary>
		/// Gibt die letzte Matrix mit den Prozenten zurück für Debugzwecke.
		/// </summary>
		public float[] LastMatrix
		{
			get
			{
				return this._lastmatrix;
			}
		}

#endif

		/// <summary>
		/// Gibt zurück, ob doppelte oder mehrmals hintereinander vorkommende Leerzeichen entfernt werden sollen, oder legt dies fest.
		/// </summary>
		public bool RemoveDoubleWhitespaces
		{
			get
			{
				return this._removeDoubleWhitespaces;
			}
			set
			{
				this._removeDoubleWhitespaces = value;
			}
		}

		/// <summary>
		/// Gibt zurück, ob nach dem ein oder mehrere Zeichen falsch gewesen sind und anschließend wieder ein richtiges Zeichen, ob dann der Zähler mit den Anzahl der falschen Zeichen zurückgesetzt werden soll, oder legt dies fest.
		/// </summary>
		public bool PutBack
		{
			get
			{
				return this._putBack;
			}
			set
			{
				this._putBack = value;
			}
		}

		/// <summary>
		/// Gibt zurück, ob die beiden übergebenen Zeichenfolgen nach ihrer Länge sortiert bzw. getauscht werden sollen oder legt dies fest. 
		/// </summary>
		public bool SortVars
		{
			get
			{
				return this._sortVars;
			}
			set
			{
				this._sortVars = false;
			}
		}

		/// <summary>
		/// Gibt zurück, ob die längere Zeichenfolge als erste Zeichenfolge nach dem sortieren bzw. tauschen festgelegt werden soll, oder legt die Eigenschaft fest.
		/// Wenn SortVars nicht auf true gesetzt ist, hat diese Eigenschaft keine Auswirkung.
		/// </summary>
		public bool LongerVarFirst
		{
			get
			{
				return this._LongerVarFirst;
			}
			set
			{
				this._LongerVarFirst = value;
			}
		}

		/// <summary>
		/// Gibt die Prozente zurück, wie weit maximal ein zu suchendes Zeichen von dem Orginal entfernt sein darf oder legt dieses fest.
		/// Eine weitere Einschränkung ist, wie oft bereits vorangegangen Zeichen nicht der Position von der anderen Zeichenfolge gestanden hat.
		/// </summary>
		public float MaxAway
		{
			get
			{
				return this._maxAway;
			}
			set
			{
				this._maxAway = value;
			}
		}

		/// <summary>
		/// Gibt zurück, ob Groß- und Kleinschreibung berücksichtigt werden soll, oder legt es fest.
		/// </summary>
		public bool IgnoreCase
		{
			get
			{
				return this._ignoreCase;
			}
			set
			{
				this._ignoreCase = value;
			}
		}

		#endregion

		#region Konstruktoren

		/// <summary>
		/// Erstellt eine neue MyStringComparer-Klasse.
		/// </summary>
		public MyStringComparer()
		{
		}

		#endregion

		#region Funktionen

		/// <summary>
		/// Vergleicht zwei Strings auf Gleichheit und gibt die Ähnlichkeit in Prozent zurück.
		/// </summary>
		/// <param name="var1">Die 1. Zeichenfolge</param>
		/// <param name="var2">Die 2. Zeichenfolge</param>
		/// <returns>Die Ähnlichkeit der beiden Zeichenfolgen in Prozent (100% = 1)</returns>
		public float IsEqual(string var1, string var2)
		{
#if !DEBUG
			float currentMatrix;				//Das aktuelle Ergebnis
#endif
			int indexVar2;						//Der aktuelle Index bei der größeren Zeichenkette
			float prozentGesamt;				//Die Gesamtprozente
			int lastNot100PercentChars;			//Die Anzahl der letzten Zeichen, die nicht 100 Prozent übereingestimmt haben

			//1. Vorbereitung
			//Doppelte Leerzeichen entfernen
			if (this._removeDoubleWhitespaces)
			{
				var1 = var1.Replace("  ", " ");
				var2 = var2.Replace("  ", " ");
			}

			//Nach Länge sortieren. 
			if (this._sortVars)
			{
				if (this._LongerVarFirst)
				{
					if (var1.Length < var2.Length)
					{
						string tmpVar = var1;
						var1 = var2;
						var2 = tmpVar;
					}
				}
				else
				{
					if (var1.Length > var2.Length)
					{
						string tmpVar = var2;
						var2 = var1;
						var1 = tmpVar;
					}
				}
			}

			if (this._ignoreCase)
			{
				var1 = var1.ToUpper();
				var2 = var2.ToUpper();
			}

#if DEBUG
			this._lastmatrix = new float[var1.Length];
#endif
			indexVar2 = 0;
			prozentGesamt = 0;
			lastNot100PercentChars = 0;

			//Vergleichen
			/*
			 * Zeichen die nicht zugeordnet werden konnten
			 */
			for (int indexVar1 = 0; indexVar1 < this._lastmatrix.Length; indexVar1++)
			{
#if !DEBUG
				currentMatrix = 0;
#endif

				char currentZeichen1 = var1[indexVar1];
				char currentZeichen2 = var2[indexVar2];

				if (var1[indexVar1] == var2[indexVar2])
				{
#if DEBUG
					this._lastmatrix[indexVar1] = 1F;
#else
					currentMatrix = 1F;
#endif

					if (this._putBack)
						lastNot100PercentChars = 0;
				}
				else
				{
					int tmpNewIndexZur = -1;
					int tmpNewIndexVor = -1;

					//Indexbestimmtung
					if (indexVar2 == 0)
						tmpNewIndexZur = -1;
					else
					{
						if (indexVar1 >= var2.Length)
							tmpNewIndexZur = var2.LastIndexOf(var1[indexVar1], var2.Length - 1);
						else
							tmpNewIndexZur = var2.LastIndexOf(var1[indexVar1], indexVar1);
					}

					if (indexVar2 == var2.Length)
						tmpNewIndexVor = -1;
					else
					{
						if (indexVar1 >= var2.Length)
						{
							if (var1[indexVar1] == var2[var2.Length - 1])
								tmpNewIndexVor = var2.Length - 1;
							else
								tmpNewIndexVor = -1;
						}
						else
							tmpNewIndexVor = var2.IndexOf(var1[indexVar1], indexVar1);
					}

					//Vergleichen, welcher Index am nächsten liegt
					int abstandVor = (int)Math.Sqrt(Math.Pow(indexVar1 - tmpNewIndexVor, 2));
					int abstandZur = (int)Math.Sqrt(Math.Pow(indexVar1 - tmpNewIndexZur, 2));

					//Neues Zeichen nicht gefunden, egal ob zurück oder vorwärts
					if (tmpNewIndexVor == -1 && tmpNewIndexZur == -1)
#if DEBUG
						this._lastmatrix[indexVar1] = 0;
#else
						currentMatrix = 0;
#endif

					//Neues Zeichen nur Rückwärts gefunden
					else if (tmpNewIndexVor == -1)
					{
						float tmpF = 1F - (float)abstandZur / var2.Length;
						if (lastNot100PercentChars > 0) tmpF = 1F - (float)lastNot100PercentChars / var1.Length;
						if (tmpF < this._maxAway) tmpF = 0;

#if DEBUG
						this._lastmatrix[indexVar1] = tmpF;
#else
						currentMatrix = tmpF;
#endif
						indexVar2 = tmpNewIndexZur;
					}

					//Neues Zeichen nur Vorwärts gefunden
					else if (tmpNewIndexZur == -1)
					{
						float tmpF = 1F - (float)abstandVor / var2.Length;
						if (lastNot100PercentChars > 0) tmpF = 1F - (float)lastNot100PercentChars / var1.Length;
						if (tmpF < this._maxAway) tmpF = 0;

#if DEBUG
						this._lastmatrix[indexVar1] = tmpF;
#else
						currentMatrix = tmpF;
#endif
						indexVar2 = tmpNewIndexVor;
					}

					//Abstand von beiden Zeichen gleich
					else if (abstandVor == abstandZur)
					{
						//Nehme das zurückliegende Zeichen
						float tmpF = 1F - (float)abstandZur / var2.Length;
						if (lastNot100PercentChars > 0) tmpF = 1F - (float)lastNot100PercentChars / var1.Length;
						if (tmpF < this._maxAway) tmpF = 0;

#if DEBUG
						this._lastmatrix[indexVar1] = tmpF;
#else
						currentMatrix = tmpF;
#endif
						indexVar2 = tmpNewIndexZur;
					}

					//Abstand zum zurückliegenden Zeichen ist größer als zum vorliegenden
					else if (abstandZur > abstandVor)
					{
						//Nächstes Zeichen nehmen
						float tmpF = 1F - (float)abstandVor / var2.Length;
						if (lastNot100PercentChars > 0) tmpF = 1F - (float)lastNot100PercentChars / var1.Length;
						if (tmpF < this._maxAway) tmpF = 0;

#if DEBUG
						this._lastmatrix[indexVar1] = tmpF;
#else
						currentMatrix = tmpF;
#endif
						indexVar2 = tmpNewIndexVor;
					}

					//Abstand zum vorliegenden Zeichen ist größer als zurückliegenden
					else if (abstandZur < abstandVor)
					{
						//Das zurückliegende Zeichen verwenden
						float tmpF = 1F - (float)abstandZur / var2.Length;
						if (lastNot100PercentChars > 0) tmpF = 1F - (float)lastNot100PercentChars / var1.Length;
						if (tmpF < this._maxAway) tmpF = 0;

#if DEBUG
						this._lastmatrix[indexVar1] = tmpF;
#else
						currentMatrix = tmpF;
#endif
						indexVar2 = tmpNewIndexZur;
					}

					lastNot100PercentChars++;
				}

#if DEBUG
				prozentGesamt += this._lastmatrix[indexVar1];
#else
				prozentGesamt += currentMatrix;
#endif

				//indexMax hochzählen
				if (indexVar2 < var2.Length - 1) indexVar2++;
			}

			//Prozente zusammenzählen
			prozentGesamt /= (float)var1.Length;

			return prozentGesamt;
		}

		#endregion
	}
}

2 Kommentare zum Snippet

dakramar schrieb am 6/17/2016:
Hallo,
wenn ich im VisualStudio auf Relase statt Debug umstelle bekomme ich folgende Fehlermeldung:

Fehler 17 "xxxxxx.xxxxxxx.MyStringComparer" enthält keine Definition für "_lastmatrix", und es konnte keine Erweiterungsmethode "_lastmatrix" gefunden werden, die ein erstes Argument vom Typ "xxxxxx.xxxxxxxx.MyStringComparer" akzeptiert. (Fehlt eine Using-Direktive oder ein Assemblyverweis?) C:\xxxx\xxxxxx\Documents\Visual Studio 2013\Projects\xxxxxxx\xxxxxxxxx\Class\ProzentualTextvergleich.cs 266 54

Bitte um Info warum das so ist?

Danke
Gruß Danijel
Koopakiller schrieb am 6/17/2016:
@Danijel In dem Snippet siehst du einige Zeilen die mit einem #-Zeichen beginnen. Das sind Anweisungen für den Compiler, sodass beispielsweise der Code zwischen folgenden 2 Zeilen nur im Debug-Modus mit kompiliert wird.
#if DEBUG
#endif

Die IsEqual-Methode greift nun einmal außerhalb eines solchen Blocks auf _lastMatrix zu. Ich würde einfach immer alles mit kompilieren (habe mir den Code nicht näher angesehen). Entferne dazu einfach alle Zeilen die mit # beginnen.
 

Logge dich ein, um hier zu kommentieren!