Programmiersprache anhand eines Snippets erkennen


114

Was wäre der beste Weg, um festzustellen, welche Programmiersprache in einem Codeausschnitt verwendet wird?


1
Es gibt praktisch unendlich viele Sprachen da draußen ... möchten Sie JEDE von ihnen entdecken? Oder reden wir nur über die populären?
Spencer Ruport

Nur die beliebtesten (C / C ++, C #, Java, Pascal, Python, VB.NET, PHP, JavaScript und vielleicht Haskell).
João Matos

12
Nun, Haskell kann nicht beliebt sein, da ich noch nie davon gehört habe. ;-)
Stephanie Seite

22
Sie wissen wahrscheinlich nicht viel über Programmiersprachen, wenn Sie noch nichts von Haskell gehört haben.
Akhorus

4
Es gibt diesen Onlinedienst, der dies tut: algorithmia.com/algorithms/PetiteProgrammer/…
Benny Neugebauer

Antworten:


99

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 defSchlüsselwort hat. Wenn es also 1000x defin Python und 100x defin Ruby gesehen hat, kann es trotzdem Python sagen, obwohl putsundend 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)

1
Ich muss es auch in Forensoftware verwenden. Vielen Dank für den Tipp zur Bayes'schen Filterung.
João Matos

12
Ich habe so etwas in meiner NLP-Klasse gemacht, aber wir sind noch einen Schritt weiter gegangen. Sie mögen es nicht, die Häufigkeit eines einzelnen Wortes zu betrachten, sondern Paare und Dreifache von Wörtern. Zum Beispiel könnte "public" in vielen Sprachen ein Schlüsselwort sein, aber "public static void" ist in C # häufiger. Wenn das Triple nicht gefunden werden kann, fallen Sie auf 2 und dann auf 1.
Mpen

1
Vielleicht möchten Sie auch darüber nachdenken, wo Sie die Wörter aufteilen. In PHP beginnen Variablen mit $, 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.
Mpen

2
Ja. Eine Möglichkeit, eine Aufteilung zu vermeiden, ist die Verwendung von ngrams: Sie nehmen jeden Teilstring mit n Länge. Zum Beispiel sind die 5 Gramm "Puts foo" "Puts", "uts f", "ts fo" und "s foo". Diese Strategie mag seltsam erscheinen, aber sie funktioniert besser als man denkt. Es ist einfach nicht so, wie ein Mensch das Problem lösen würde. Um zu entscheiden, welche Methode besser funktioniert, müssen Sie beide testen ...
Jules

2
Einige Sprachen haben jedoch sehr wenig Syntax. Ich spekuliere auch, dass allgemeine Variablennamen die Schlüsselwörter der Sprache dominieren würden. Wenn Sie einen von einem Ungar geschriebenen C-Code mit Variablennamen und Kommentaren auf Ungarisch in Ihren Trainingsdaten haben, wird jede andere Quelle mit Ungarisch wahrscheinlich als "ähnlich" eingestuft.
Tripleee

26

Spracherkennung von anderen gelöst:

Ohlohs Ansatz: https://github.com/blackducksw/ohcount/

Githubs Ansatz: https://github.com/github/linguist


4
Ich habe beide Lösungen untersucht und keiner wird genau das tun, was gefragt wurde. Sie untersuchen hauptsächlich die Dateierweiterungen, um die Sprache zu bestimmen, sodass sie ein Snippet nicht unbedingt ohne einen Hinweis auf die Erweiterung untersuchen können.
Hawkee

5
Githubs Ansatz beinhaltet jetzt auch einen Bayes'schen Klassifikator. Es erkennt in erster Linie einen Sprachkandidaten basierend auf der Dateierweiterung. Wenn eine Dateierweiterung jedoch mehreren Kandidaten entspricht (z. B. ".h" -> C, C ++, ObjC), wird das Beispiel für den Eingabecode tokenisiert und anhand eines vorab trainierten Satzes klassifiziert von Dateien. Die Github-Version kann gezwungen werden, den Code immer zu scannen, ohne auch die Erweiterung zu betrachten.
Benzi



5

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.


26
Wenn mehrere Sprachen möglich sind, kann der Detektor einfach alle möglichen Kandidaten angeben.
Steven Haryanto

Oder es kann den ersten geben, der passt. Wenn der reale Anwendungsfall so etwas wie das Hervorheben der Syntax ist, würde dies wirklich keinen Unterschied machen. Dies bedeutet, dass jede der übereinstimmenden Sprachen dazu führen würde, dass der Code korrekt hervorgehoben wird.
Jonschlinkert

5

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.


Die in Highlight.js enthaltenen Sprachdaten sind auf die zum Hervorheben erforderlichen Werte beschränkt, was sich für die Spracherkennung als unzureichend herausstellt (insbesondere für kleine Codemengen).
Adam Kennedy

Ich denke, es ist in Ordnung, überprüfen Sie mit dieser Geige jsfiddle.net/3tgjnz10
sebilasse

4

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...

3
Das Problem ist, dass diese Schlüsselwörter weiterhin in jeder Sprache angezeigt werden können, entweder als Variablennamen oder in Zeichenfolgen. Das und es gibt viele Überschneidungen bei den verwendeten Schlüsselwörtern. Sie müssten mehr tun, als nur nach Schlüsselwörtern zu suchen.
Mpen

2

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.


Alle Sprachen können nicht einmal von einem BNF beschrieben werden. Wenn Sie Schlüsselwörter neu definieren und Makros erstellen dürfen, wird es viel schwieriger. Alså, da es sich um ein Snippet handelt, müssten Sie ein Teil-Match gegen einen BNF durchführen, der schwieriger und fehleranfälliger ist.

2

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.


1

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.


1
Bei näherer Betrachtung scheint es so, als ob Prettify die Sprache nicht erkennt, sondern sie entsprechend der Syntax jedes Elements hervorhebt.
Hawkee


1

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.


0

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.


0

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:

  • Funktionsdefinitionen
  • Variablendeklarationen
  • Klassendeklarationen
  • Bemerkungen
  • für Schleifen
  • while-Schleifen
  • Anweisungen drucken

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.


0

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.


0

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 Gemfilein 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-devversuchen Sie es erneut).

(Möglicherweise müssen Sie ein sudo apt-get updateoder sudo apt-get install makeausführen, sudo apt-get install build-essentialwenn 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.tplaber 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 --breakdownSie 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.


0

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 defSchlü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 endauch 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.


0

Richten Sie den zufälligen Scrambler wie ein

matrix S = matrix(GF(2),k,[random()<0.5for _ in range(k^2)]); while (rank(S) < k) : S[floor(k*random()),floor(k*random())] +=1;

0

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/

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.