Plagiat oder nicht?
Geschrieben von: Christoph Wille Ob das Aussehen einer Website, die Funktionalität eines Programmes, überall steht Sourcecode dahinter - von HTML bis C#. Stellen Sie sich vor, es kommt Ihnen zufällig ein Sourcecode unter, der Sie ganz stark an Ihren eigenen aus einem Projekt erinnert... so ist es uns, dem SharpDevelop Team, passiert. Die Frage die sich nun stellt, wie weist man nach daß geklaut wurde? Ein Weg ist, mühsam jedes Sourcecodefile händisch zu vergleichen, das verbietet sich allerdings ab einer gewissen Zahl an Sourcecodezeilen - die Stellen des Plagiarismus wollen eingegrenzt werden, bevor man sich die Mühe des händischen Vergleichens macht. Ein sehr robuster Weg um Plagiarismus aufzudecken ist die Verwendung des Longest Common Substring Algorithmus, der für die Erkennung von Gleichheiten in DNA Sequenzen verwendet wird. Dieser Algorithmus ist nicht ganz ohne Seitenhieb auf unser Problem: wir lernen, was im Laufe der Evolution an Genen erhalten geblieben ist. Das LCS ToolIm Download für diesen Artikel mit dabei ist das LCS Tool als Kommandozeilenversion. Als Wort der Warnung: die Pfade für die zu vergleichenden Dateien sind hardcodiert, aber da der Sourcecode mitgeliefert wird, ist die Anpassung eine Kleinigkeit. Die gesamte LCS Funktionalität ist in einer passend LCS genannten Klasse verpackt: namespace Algorithms { public class LCS { private string ReadFile(string fileName); private int Compute(ref string src, ref string dst, int srcOffset, int dstOffset); public void Compare(string srcFile, string dstFile); public void CompareSet(string[] src, string[] dst); } } Bevor wir uns Sourcecode für die Klasse selbst ansehen, wie verwendet man die Klasse? Hier ein Beispiel: string[] sourceFiles = new string[] { @"AssemblyInfo1.cs" }; string[] destinationFiles = new string[] { @"AssemblyInfo2.cs" }; LCS lcs = new LCS(); lcs.CompareSet(sourceFiles, destinationFiles); Die Methode CompareSet kann mehrere Dateien vergleichen, die per Array übergeben werden. Intern ruft sie die Methode Compare für jedes Set von Dateien (Source und potentielles Plagiat) auf - diese liest dann die Dateien aus (ReadFile löscht zusätzlich die Whitespaces) und errechnet den LCS Wert (Compute): den längsten gefundenen Substring, und daraus die Wahrscheinlichkeit, daß es sich tatsächlich um ein Plagiat handelt: public void Compare(string srcFile, string dstFile) { string src = ReadFile(srcFile); string dst = ReadFile(dstFile); table = new int[src.Length, dst.Length]; for (int i =0; i < src.Length; ++i) { for (int j =0; j < dst.Length; ++j) { table[i, j] = -1; } } Console.WriteLine("source: {0}, destination: {1}", Path.GetFileName(srcFile), Path.GetFileName(dstFile)); Console.WriteLine("source length: {0}, destination length: {1}", src.Length, dst.Length); int lcs = Compute(ref src, ref dst, 0, 0); Console.WriteLine("LCS match: {0}/{1}%", lcs, (lcs * 100) / dst.Length); } In unserem Drill-Down in die Berechnung des LCS fehlt uns jetzt nur noch die Compute Methode: private int Compute(ref string src, ref string dst, int srcOffset, int dstOffset) { if (srcOffset >= src.Length || dstOffset >= dst.Length) { return 0; } if (table[srcOffset, dstOffset] == -1) { if (Char.ToUpper(src[srcOffset]) == Char.ToUpper(dst[dstOffset])) { table[srcOffset, dstOffset] = 1 + Compute(ref src, ref dst, srcOffset + 1, dstOffset + 1); } else { table[srcOffset, dstOffset] = Math.Max(Compute(ref src, ref dst, srcOffset, dstOffset + 1), Compute(ref src, ref dst, srcOffset + 1, dstOffset)); } } return table[srcOffset, dstOffset]; } Es wird die private Variable table verwendet, die zuvor in der Compare Methode mit -1en gefüllt wurde. Was wird hier berechnet? Der längste zusammenhängende Substring. Und das passiert indem man die beiden Dateien (mittlerweile sind es Strings) Zeichen für Zeichen durchgeht, und mit Überspringen von Abweichungen den längsten gemeinsamen String herausrechnet (mathematische Details in Longest Common Substring). Damit hat man dann eine Wahrscheinlichkeit an der Hand, mit der man eine sehr gute Aussage treffen kann, ob ein Sourcecode von einem anderen "inspiriert" wurde (die berühmte Blaupause). AnwendungUm zu demonstrieren wie das Programm arbeitet, habe ich eine von VS.NET generierte AssemblyInfo.cs Datei genommen, und diese Datei zweimal kopiert. Eine Datei (AssemblyInfo1.cs) wurde nicht verändert, und in der zweiten (AssemblyInfo2.cs) nur das AssemblyCompany Attribut mit einem Wert befüllt: C:\lcs\bin\Debug>lcs source: AssemblyInfo1.cs, destination: AssemblyInfo2.cs source length: 1908, destination length: 1923 LCS match: 1908/99% Tja, ich würde sagen da hat wer mein AssemblyInfo1.cs genommen und nur einen String eingefügt. Plagiat. Als nächstes habe ich aus AssemblyInfo2.cs alle Kommentare gelöscht, und das Tool nochmals gestartet: C:\lcs\bin\Debug>lcs source: AssemblyInfo1.cs, destination: AssemblyInfo2.cs source length: 1908, destination length: 457 LCS match: 442/96% So ein Pech aber auch, eine einfache Kommentarkosmetik wird immer noch durch das Tool entlarvt. Ich erspare mir jetzt weitere Demonstrationen, Sie sehen ja schon in welche Richtung das läuft - man hat mit LCS eine sehr gute Möglichkeit, Plagiatskandidaten zu erforschen. Aus unserer Erfahrung sollte man sich alles ab 60% Wahrscheinlichkeit anschauen, allerdings Vorsicht bei Dateien die kaum mehr als eine Zeile Code pro Methode beinhalten: hier läuft man Gefahr, Implementierungen von Interfaces die einfach nicht anders bewerkstelligt werden können als Plagiate mißzuinterpretieren. Gleiches gilt für durch Wizards autogenerierte Klassenstubs und dergleichen. Hat man Plagiatskandidaten gefunden, muß man den Code aber dennoch durchsehen - und da findet man schon die eine oder andere Eigenheit des Programmierers, die der Kopierer "miteingeschleppt" hat. Bei uns waren das Bugs spezifisch zum Framework das bei uns rund um den geklauten Code verwendet wurde, etliche Eigenheiten des Programmierers und andere Kleinigkeiten. SchlußbemerkungHat man den Code analysiert kommt dann der eigentlich nervenaufreibende Teil: denjenigen zu kontaktieren, der den Code geklaut hat, es aber sicher nicht sofort zugeben wird. Hat man aber eine solide Analyse (und LCS mit seinen harten Wahrscheinlichkeiten tut mehr "weh" als die Fakten die man durch händische Nachanalyse gefunden hat), dann bringt man den Inspirierten auch dazu, den Code zu ändern. Download des CodesKlicken Sie hier, um den Download zu starten. Links zu anderen Sites
A matter of inspiration Wenn Sie jetzt Fragen haben...Wenn Sie Fragen rund um die in diesem Artikel vorgestellte Technologie haben, dann schauen Sie einfach bei uns in den Community Foren der deutschen .NET Community vorbei. Die Teilnehmer helfen Ihnen gerne, wenn Sie sich zur im Artikel vorgestellten Technologie weiterbilden möchten. Eine weitere sehr hilfreiche Resource ist das deutsche ASP.NET Wiki, das als zentrale Anlaufstelle für Tips, Tricks, Know How und alles Nützliche was man in seinem Alltag als (ASP).NET-Entwickler so braucht und entdeckt gedacht ist. Haben Sie Fragen die sich direkt auf den Inhalt des Artikels beziehen, dann schreiben Sie dem Autor! Unsere Autoren freuen sich über Feedback zu ihren Artikeln. Ein einfacher Klick auf die Autor kontaktieren Schaltfläche (weiter unten) und schon haben Sie ein für diesen Artikel personalisiertes Anfrageformular.
Und zu guter Letzt möchten wir Sie bitten, den Artikel zu bewerten. Damit helfen Sie uns, die Qualität der Artikel zu verbessern - und anderen Lesern bei der Auswahl der Artikel, die sie lesen sollten.
©2000-2006 AspHeute.com |