Was wäre der beste Weg, um festzustellen, welche Programmiersprache in einem Codeausschnitt verwendet wird?
Was wäre der beste Weg, um festzustellen, welche Programmiersprache in einem Codeausschnitt verwendet wird?
Antworten:
Ich denke, dass die in Spam-Filtern verwendete Methode sehr gut funktionieren würde. Sie teilen das Snippet in Wörter auf. Anschließend vergleichen Sie das Vorkommen dieser Wörter mit bekannten Snippets und berechnen die Wahrscheinlichkeit, dass dieses Snippet in Sprache X für jede Sprache geschrieben ist, an der Sie interessiert sind.
http://en.wikipedia.org/wiki/Bayesian_spam_filtering
Wenn Sie über den grundlegenden Mechanismus verfügen, können Sie ganz einfach neue Sprachen hinzufügen: Trainieren Sie den Detektor einfach mit ein paar Ausschnitten in der neuen Sprache (Sie können ihm ein Open-Source-Projekt zuführen). Auf diese Weise erfährt es, dass "System" wahrscheinlich in C # -Schnipsel und "Puts" in Ruby-Schnipsel erscheint.
Ich habe diese Methode tatsächlich verwendet, um Codefragmenten für Forensoftware eine Spracherkennung hinzuzufügen. Es funktionierte 100% der Zeit, außer in mehrdeutigen Fällen:
print "Hello"
Lassen Sie mich den Code finden.
Ich konnte den Code nicht finden und habe einen neuen erstellt. Es ist ein bisschen simpel, aber es funktioniert für meine Tests. Derzeit, wenn Sie es viel mehr Python-Code als Ruby-Code füttern, ist es wahrscheinlich, dass dieser Code:
def foo
puts "hi"
end
ist Python-Code (obwohl es wirklich Ruby ist). Dies liegt daran, dass Python auch ein def
Schlüsselwort hat. Wenn es also 1000x def
in Python und 100x def
in Ruby gesehen hat, kann es trotzdem Python sagen, obwohl puts
undend
Ruby-spezifisch. Sie können dies beheben, indem Sie die pro Sprache gesehenen Wörter verfolgen und irgendwo durch diese dividieren (oder indem Sie in jeder Sprache gleiche Codemengen eingeben).
Ich hoffe es hilft dir:
class Classifier
def initialize
@data = {}
@totals = Hash.new(1)
end
def words(code)
code.split(/[^a-z]/).reject{|w| w.empty?}
end
def train(code,lang)
@totals[lang] += 1
@data[lang] ||= Hash.new(1)
words(code).each {|w| @data[lang][w] += 1 }
end
def classify(code)
ws = words(code)
@data.keys.max_by do |lang|
# We really want to multiply here but I use logs
# to avoid floating point underflow
# (adding logs is equivalent to multiplication)
Math.log(@totals[lang]) +
ws.map{|w| Math.log(@data[lang][w])}.reduce(:+)
end
end
end
# Example usage
c = Classifier.new
# Train from files
c.train(open("code.rb").read, :ruby)
c.train(open("code.py").read, :python)
c.train(open("code.cs").read, :csharp)
# Test it on another file
c.classify(open("code2.py").read) # => :python (hopefully)
$
, daher sollten Sie sich vielleicht nicht auf Wortgrenzen aufteilen, da diese $
bei der Variablen bleiben sollten. Operatoren wie =>
und :=
sollte fest zusammen als ein einziges Token sein, aber OTH Sie wahrscheinlich sollte geteilt um {
s , weil sie immer für sich allein stehen.
Spracherkennung von anderen gelöst:
Ohlohs Ansatz: https://github.com/blackducksw/ohcount/
Githubs Ansatz: https://github.com/github/linguist
Möglicherweise finden Sie hier nützliches Material: http://alexgorbatchev.com/wiki/SyntaxHighlighter . Alex hat viel Zeit damit verbracht, herauszufinden, wie man eine große Anzahl verschiedener Sprachen analysiert und welche Schlüsselsyntaxelemente es gibt.
Guesslang ist eine mögliche Lösung:
http://guesslang.readthedocs.io/en/latest/index.html
Es gibt auch SourceClassifier:
https://github.com/chrislo/sourceclassifier/tree/master
Ich interessierte mich für dieses Problem, nachdem ich in einem Blog-Artikel Code gefunden hatte, den ich nicht identifizieren konnte. Das Hinzufügen dieser Antwort, da diese Frage der erste Suchtreffer nach "Programmiersprache identifizieren" war.
Es ist sehr schwer und manchmal unmöglich. Aus welcher Sprache stammt dieser kurze Ausschnitt?
int i = 5;
int k = 0;
for (int j = 100 ; j > i ; i++) {
j = j + 1000 / i;
k = k + i * j;
}
(Hinweis: Es könnte einer von mehreren sein.)
Sie können versuchen, verschiedene Sprachen zu analysieren und mithilfe der Frequenzanalyse von Schlüsselwörtern zu entscheiden. Wenn bestimmte Schlüsselwortsätze mit bestimmten Häufigkeiten in einem Text vorkommen, ist es wahrscheinlich, dass die Sprache Java usw. ist. Ich glaube jedoch nicht, dass Sie etwas erhalten, das völlig narrensicher ist, da Sie beispielsweise eine Variable in C mit demselben Namen benennen könnten als Schlüsselwort in Java, und die Frequenzanalyse wird getäuscht.
Wenn Sie die Komplexität verbessern, können Sie nach Strukturen suchen. Wenn ein bestimmtes Schlüsselwort immer nach einem anderen folgt, erhalten Sie mehr Hinweise. Es wird aber auch viel schwieriger zu entwerfen und zu implementieren sein.
Eine Alternative ist die Verwendung von refresh.js , die eine Syntaxhervorhebung durchführt, jedoch die Erfolgsrate des Hervorhebungsprozesses verwendet, um die Sprache zu identifizieren. Grundsätzlich kann jede Syntax-Textmarker-Codebasis auf die gleiche Weise verwendet werden, aber das Schöne an Highlight.js ist, dass die Spracherkennung als Funktion betrachtet und zu Testzwecken verwendet wird .
UPDATE: Ich habe es versucht und es hat nicht so gut funktioniert. Komprimiertes JavaScript hat es völlig verwirrt, dh der Tokenizer ist Whitespace-empfindlich. Im Allgemeinen scheint es nicht sehr zuverlässig zu sein, nur Highlight-Treffer zu zählen. Ein stärkerer Parser oder möglicherweise nicht übereinstimmende Abschnittszahlen funktionieren möglicherweise besser.
Zuerst würde ich versuchen, die spezifischen Schlüsselwerke einer Sprache zu finden, z
"package, class, implements "=> JAVA
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...
Es würde davon abhängen, welche Art von Snippet Sie haben, aber ich würde es durch eine Reihe von Tokenizern laufen lassen und sehen, gegen welche BNF-Sprache es gültig war.
Schönes Puzzle.
Ich denke, es ist unmöglich, alle Sprachen zu erkennen. Sie können jedoch wichtige Token auslösen. (bestimmte reservierte Wörter und häufig verwendete Zeichenkombinationen).
Ben gibt es viele Sprachen mit ähnlicher Syntax. Es kommt also auf die Größe des Snippets an.
Prettify ist ein Javascript-Paket, mit dem Programmiersprachen gut erkannt werden können:
http://code.google.com/p/google-code-prettify/
Es handelt sich hauptsächlich um einen Syntax-Textmarker, aber es gibt wahrscheinlich eine Möglichkeit, den Erkennungsteil zu extrahieren, um die Sprache aus einem Snippet zu erkennen.
Ich brauchte das, also habe ich mein eigenes erstellt. https://github.com/bertyhell/CodeClassifier
Es ist sehr einfach zu erweitern, indem eine Trainingsdatei im richtigen Ordner hinzugefügt wird. Geschrieben in c #. Aber ich stelle mir vor, dass der Code leicht in eine andere Sprache konvertiert werden kann.
Ich würde nicht glauben, dass es einen einfachen Weg gibt, dies zu erreichen. Ich würde wahrscheinlich Listen von Symbolen / allgemeinen Schlüsselwörtern generieren, die für bestimmte Sprachen / Sprachklassen eindeutig sind (z. B. geschweifte Klammern für die Sprache im C-Stil, die Schlüsselwörter Dim und Sub für BASIC-Sprachen, das Schlüsselwort def für Python, das Schlüsselwort let für funktionale Sprachen). . Sie können dann möglicherweise grundlegende Syntaxfunktionen verwenden, um sie noch weiter einzugrenzen.
Ich denke, der größte Unterschied zwischen Sprachen ist ihre Struktur. Meine Idee wäre es also, bestimmte gemeinsame Elemente in allen Sprachen zu betrachten und zu sehen, wie sie sich unterscheiden. Sie können beispielsweise reguläre Ausdrücke verwenden, um Dinge auszuwählen wie:
Und vielleicht ein paar andere Dinge, die die meisten Sprachen haben sollten. Verwenden Sie dann ein Punktesystem. Vergeben Sie für jedes Element höchstens 1 Punkt, wenn der reguläre Ausdruck gefunden wird. Offensichtlich verwenden einige Sprachen genau dieselbe Syntax (denn Schleifen werden oft so geschrieben, for(int i=0; i<x; ++i)
dass mehrere Sprachen jeweils einen Punkt für dieselbe Sache erzielen können, aber zumindest verringern Sie die Wahrscheinlichkeit, dass es sich um eine völlig andere Sprache handelt). Einige von ihnen könnten auf der ganzen Linie 0 Punkte erzielen (das Snippet enthält zum Beispiel überhaupt keine Funktion), aber das ist vollkommen in Ordnung.
Kombinieren Sie dies mit Jules 'Lösung, und es sollte ziemlich gut funktionieren. Suchen Sie möglicherweise auch nach Häufigkeiten von Schlüsselwörtern für einen zusätzlichen Punkt.
Interessant. Ich habe eine ähnliche Aufgabe, Text in verschiedenen Formaten zu erkennen. YAML-, JSON-, XML- oder Java-Eigenschaften? Selbst bei Syntaxfehlern sollte ich beispielsweise JSON sicher von XML unterscheiden.
Ich denke, wie wir das Problem modellieren, ist kritisch. Wie Mark sagte, ist eine Einzelwort-Tokenisierung notwendig, aber wahrscheinlich nicht genug. Wir werden Bigrams oder sogar Trigramme brauchen. Aber ich denke, wir können noch weiter gehen und wissen, dass wir uns mit Programmiersprachen befassen. Ich stelle fest, dass fast jede Programmiersprache zwei einzigartige Arten von Token hat - Symbole und Schlüsselwörter . Symbole sind relativ leicht zu erkennen (einige Symbole sind möglicherweise Literale, die nicht Teil der Sprache sind). Dann nehmen Bigramme oder Trigramme von Symbolen eindeutige Syntaxstrukturen um Symbole auf. Schlüsselwörter sind ein weiteres einfaches Ziel, wenn das Trainingsset groß und vielfältig genug ist. Eine nützliche Funktion könnten Bigrams um mögliche Schlüsselwörter sein. Eine andere interessante Art von Token ist Leerzeichen. Wenn wir auf die übliche Weise durch Leerzeichen tokenisieren, verlieren wir diese Informationen. Ich würde sagen, für die Analyse von Programmiersprachen behalten wir die Whitespace-Token bei, da diese nützliche Informationen über die Syntaxstruktur enthalten können.
Wenn ich schließlich einen Klassifikator wie Random Forest wähle, werde ich Github crawlen und den gesamten öffentlichen Quellcode sammeln. Der größte Teil der Quellcodedatei kann mit einem Dateisuffix gekennzeichnet werden. Für jede Datei werde ich sie zufällig in leere Zeilen in Ausschnitte unterschiedlicher Größe aufteilen. Ich werde dann die Features extrahieren und den Klassifikator mit den beschrifteten Snippets trainieren. Nach Abschluss des Trainings kann der Klassifikator auf Präzision und Rückruf getestet werden.
Die beste Lösung, auf die ich gestoßen bin, ist die Verwendung des Sprachjuwelen in einer Ruby on Rails-App. Es ist eine bestimmte Art, es zu tun, aber es funktioniert. Dies wurde oben von @nisc erwähnt, aber ich werde Ihnen meine genauen Schritte für die Verwendung mitteilen. (Einige der folgenden Befehlszeilenbefehle sind spezifisch für Ubuntu, sollten jedoch leicht in andere Betriebssysteme übersetzt werden können.)
Wenn Sie eine Rails-App haben, mit der Sie vorübergehend nichts anfangen möchten, erstellen Sie eine neue Datei, um das betreffende Code-Snippet einzufügen. (Wenn Sie keine Rails installiert haben, gibt es hier eine gute Anleitung , obwohl ich dies für Ubuntu empfehle . Führen Sie dann eine rails new <name-your-app-dir>
CD aus und kopieren Sie sie in dieses Verzeichnis. Alles, was Sie zum Ausführen einer Rails-App benötigen, ist bereits vorhanden.)
Nachdem Sie eine Rails-App haben, mit der Sie diese verwenden können, fügen Sie sie gem 'github-linguist'
zu Ihrer Gemfile hinzu (buchstäblich nur Gemfile
in Ihrem App-Verzeichnis aufgerufen , keine ext).
Dann installiere ruby-dev ( sudo apt-get install ruby-dev
)
Dann installiere cmake ( sudo apt-get install cmake
)
Jetzt können Sie ausführen gem install github-linguist
(wenn Sie eine Fehlermeldung erhalten, die besagt, dass icu erforderlich ist, sudo apt-get install libicu-dev
versuchen Sie es erneut).
(Möglicherweise müssen Sie ein sudo apt-get update
oder sudo apt-get install make
ausführen, sudo apt-get install build-essential
wenn dies nicht funktioniert hat.)
Jetzt ist alles eingerichtet. Sie können dies jetzt jederzeit verwenden, wenn Sie Codefragmente überprüfen möchten. Öffnen Sie in einem Texteditor die Datei, die Sie zum Einfügen Ihres Code-Snippets erstellt haben (sagen wir einfach, app/test.tpl
aber wenn Sie die Erweiterung Ihres Snippets kennen, verwenden Sie diese anstelle von .tpl
. Wenn Sie die Erweiterung nicht kennen, verwenden Sie keine ). Fügen Sie nun Ihr Code-Snippet in diese Datei ein. Gehen Sie zur Befehlszeile und führen Sie sie aus bundle install
(muss sich im Verzeichnis Ihrer Anwendung befinden). Dann laufen linguist app/test.tpl
(allgemeiner linguist <path-to-code-snippet-file>
). Hier erfahren Sie den Typ, den MIME-Typ und die Sprache. Für mehrere Dateien (oder für die allgemeine Verwendung mit einer Ruby / Rails-App) können bundle exec linguist --breakdown
Sie im Verzeichnis Ihrer Anwendung ausführen .
Es scheint eine Menge zusätzlicher Arbeit zu sein, besonders wenn Sie noch keine Schienen haben, aber Sie müssen eigentlich nichts über Schienen wissen, wenn Sie diese Schritte befolgen, und ich habe einfach keinen besseren Weg gefunden, die zu erkennen Sprache eines Datei- / Code-Snippets.
Ich glaube, dass es keine einzige Lösung gibt, die möglicherweise identifizieren könnte, in welcher Sprache sich ein Snippet befindet, nur basierend auf diesem einzelnen Snippet. Nimm das Schlüsselwort print
. Es kann in einer beliebigen Anzahl von Sprachen angezeigt werden, die jeweils unterschiedlichen Zwecken dienen und unterschiedliche Syntax haben.
Ich habe einige Ratschläge. Derzeit schreibe ich einen kleinen Code für meine Website, mit dem Programmiersprachen identifiziert werden können. Wie die meisten der anderen Beiträge, könnte es sein , große Bereich Sprachen zu programmieren , dass Sie einfach nicht gehört, können Sie nicht für sie berücksichtigen alle.
Was ich getan habe ist, dass jede Sprache durch eine Auswahl von Schlüsselwörtern identifiziert werden kann. Zum Beispiel könnte Python auf verschiedene Arten identifiziert werden. Es ist wahrscheinlich einfacher, wenn Sie "Merkmale" auswählen, die sicherlich auch für die Sprache einzigartig sind. Für Python wähle ich die Eigenschaft, Doppelpunkte zu verwenden, um eine Reihe von Anweisungen zu starten, von denen ich glaube, dass sie eine ziemlich einzigartige Eigenschaft sind (korrigieren Sie mich, wenn ich falsch liege).
Wenn Sie in meinem Beispiel keinen Doppelpunkt zum Starten eines Anweisungssatzes finden, wechseln Sie zu einem anderen möglichen Merkmal, indem Sie beispielsweise das def
Schlüsselwort zum Definieren einer Funktion verwenden. Dies kann nun einige Probleme verursachen, da Ruby auch das Schlüsselwort verwendet def
, um eine Funktion zu definieren. Der Schlüssel, um die beiden (Python und Ruby) voneinander zu unterscheiden, besteht darin, verschiedene Filterstufen zu verwenden, um die beste Übereinstimmung zu erzielen. Ruby verwendet das Schlüsselwort end
, um eine Funktion zu beenden, während Python nichts zum Beenden einer Funktion hat, nur einen Einzug, aber Sie möchten nicht dorthin gehen. Aber end
auch hier könnte Lua eine weitere Programmiersprache sein, die dem Mix hinzugefügt werden kann.
Sie können sehen, dass Programmiersprachen einfach zu viel überlagern. Ein Schlüsselwort, das ein Schlüsselwort in einer Sprache sein könnte, könnte zufällig ein Schlüsselwort in einer anderen Sprache sein. Die Verwendung einer Kombination von Schlüsselwörtern, die häufig zusammenpassen, wie z. B. Java, public static void main(String[] args)
hilft, diese Probleme zu beseitigen.
Wie ich bereits sagte, besteht Ihre beste Chance darin, nach relativ eindeutigen Schlüsselwörtern oder Schlüsselwortsätzen zu suchen, um sie voneinander zu trennen. Und wenn Sie es falsch verstehen, haben Sie es zumindest versucht.
Diese Website scheint ziemlich gut darin zu sein, Sprachen zu identifizieren, wenn Sie einen Snippet schnell in ein Webformular einfügen möchten, anstatt dies programmgesteuert zu tun: http://dpaste.com/