Quelle serait la meilleure façon de détecter le langage de programmation utilisé dans un extrait de code?
Quelle serait la meilleure façon de détecter le langage de programmation utilisé dans un extrait de code?
Réponses:
Je pense que la méthode utilisée dans les filtres anti-spam fonctionnerait très bien. Vous divisez l'extrait en mots. Ensuite, vous comparez les occurrences de ces mots avec des extraits connus et calculez la probabilité que cet extrait soit écrit dans la langue X pour chaque langue qui vous intéresse.
http://en.wikipedia.org/wiki/Bayesian_spam_filtering
Si vous avez le mécanisme de base, alors il est très facile d'ajouter de nouvelles langues: il suffit de former le détecteur avec quelques extraits de code dans la nouvelle langue (vous pouvez le nourrir d'un projet open source). De cette façon, il apprend que "System" est susceptible d'apparaître dans les extraits de code C # et de "met" dans les extraits de code Ruby.
J'ai en fait utilisé cette méthode pour ajouter une détection de langue aux extraits de code pour les logiciels de forum. Cela a fonctionné 100% du temps, sauf dans des cas ambigus:
print "Hello"
Laissez-moi trouver le code.
Je n'ai pas trouvé le code alors j'en ai créé un nouveau. C'est un peu simpliste mais ça marche pour mes tests. Actuellement, si vous lui donnez beaucoup plus de code Python que de code Ruby, il est probable que ce code:
def foo
puts "hi"
end
est du code Python (bien que ce soit vraiment Ruby). C'est parce que Python a aussi un def
mot - clé. Donc, s'il a vu 1000x def
en Python et 100x def
en Ruby, il peut toujours dire Python même si puts
et end
est spécifique à Ruby. Vous pouvez résoudre ce problème en gardant une trace des mots vus par langue et en les divisant par cela quelque part (ou en lui fournissant des quantités égales de code dans chaque langue).
J'espère que ça t'aide:
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)
$
, alors peut-être que vous ne devriez pas vous séparer sur des limites de mots, car le $
devrait rester avec la variable. Les opérateurs aiment =>
et :=
devraient être collés ensemble comme un seul jeton, mais OTH vous devriez probablement vous séparer autour de {
s car ils sont toujours autonomes.
Détection de la langue résolue par d'autres:
L'approche d'Ohloh: https://github.com/blackducksw/ohcount/
L'approche de Github: https://github.com/github/linguist
Vous trouverez peut-être des informations utiles ici: http://alexgorbatchev.com/wiki/SyntaxHighlighter . Alex a passé beaucoup de temps à comprendre comment analyser un grand nombre de langages différents et quels sont les éléments de syntaxe clés.
Guesslang est une solution possible:
http://guesslang.readthedocs.io/en/latest/index.html
Il y a aussi SourceClassifier:
https://github.com/chrislo/sourceclassifier/tree/master
Je me suis intéressé à ce problème après avoir trouvé du code dans un article de blog que je n'ai pas pu identifier. Ajout de cette réponse puisque cette question était le premier résultat de recherche pour "identifier le langage de programmation".
C'est très difficile et parfois impossible. De quelle langue provient ce court extrait?
int i = 5;
int k = 0;
for (int j = 100 ; j > i ; i++) {
j = j + 1000 / i;
k = k + i * j;
}
(Indice: il peut s'agir de n'importe lequel d'entre eux.)
Vous pouvez essayer d'analyser différentes langues et essayer de décider en utilisant l'analyse de fréquence des mots-clés. Si certains ensembles de mots-clés se produisent avec certaines fréquences dans un texte, il est probable que le langage soit Java, etc. comme mot-clé en Java, et l'analyse de fréquence sera trompée.
Si vous augmentez d'un cran la complexité, vous pouvez rechercher des structures, si un certain mot-clé vient toujours après un autre, cela vous donnera plus d'indices. Mais il sera également beaucoup plus difficile à concevoir et à mettre en œuvre.
Une alternative consiste à utiliser highlight.js , qui effectue la coloration syntaxique mais utilise le taux de réussite du processus de mise en évidence pour identifier la langue. En principe, n'importe quelle base de code de surligneur de syntaxe peut être utilisée de la même manière, mais la bonne chose à propos de highlight.js est que la détection de la langue est considérée comme une fonctionnalité et est utilisée à des fins de test .
MISE À JOUR: J'ai essayé ceci et cela n'a pas très bien fonctionné. Le JavaScript compressé l'a complètement confondu, c'est-à-dire que le tokenizer est sensible aux espaces. En règle générale, il ne semble pas très fiable de compter uniquement les hits de faits saillants. Un analyseur plus puissant, ou peut-être un nombre de sections inégalé, pourrait mieux fonctionner.
Tout d'abord, j'essaierais de trouver les keyworks spécifiques d'une langue, par exemple
"package, class, implements "=> JAVA
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...
Cela dépendrait du type d'extrait de code que vous avez, mais je le ferais passer par une série de tokenizers et verrais dans quelle langue le BNF est valide.
Joli puzzle.
Je pense qu'il est impossible de détecter toutes les langues. Mais vous pouvez déclencher sur des jetons clés. (certains mots réservés et combinaisons de caractères souvent utilisées).
Ben il y a beaucoup de langages avec une syntaxe similaire. Cela dépend donc de la taille de l'extrait.
Prettify est un package Javascript qui détecte correctement les langages de programmation:
http://code.google.com/p/google-code-prettify/
Il s'agit principalement d'un surligneur de syntaxe, mais il existe probablement un moyen d'extraire la partie de détection afin de détecter la langue à partir d'un extrait de code.
J'en avais besoin alors j'ai créé le mien. https://github.com/bertyhell/CodeClassifier
Il est très facilement extensible en ajoutant un fichier de formation dans le bon dossier. Écrit en c #. Mais j'imagine que le code est facilement converti dans n'importe quelle autre langue.
Je ne pense pas qu'il y aurait un moyen facile d'accomplir cela. Je générerais probablement des listes de symboles / mots-clés communs uniques à certaines langues / classes de langues (par exemple, les accolades pour le langage de style C, les mots-clés Dim et Sub pour les langages BASIC, le mot-clé def pour Python, le mot-clé let pour les langages fonctionnels) . Vous pourrez alors peut-être utiliser les fonctionnalités de syntaxe de base pour le réduire encore plus.
Je pense que la plus grande distinction entre les langues est sa structure. Mon idée serait donc d'examiner certains éléments communs dans toutes les langues et de voir en quoi ils diffèrent. Par exemple, vous pouvez utiliser des expressions régulières pour sélectionner des éléments tels que:
Et peut-être quelques autres choses que la plupart des langues devraient avoir. Ensuite, utilisez un système de points. Attribuez au plus 1 point pour chaque élément si l'expression régulière est trouvée. De toute évidence, certaines langues utiliseront exactement la même syntaxe (les boucles for sont souvent écrites comme for(int i=0; i<x; ++i)
si plusieurs langues pourraient chacune marquer un point pour la même chose, mais au moins vous réduisez la probabilité qu'il s'agisse d'une langue entièrement différente). Certains d'entre eux peuvent obtenir des scores de 0 dans tous les domaines (l'extrait de code ne contient pas du tout de fonction, par exemple), mais c'est parfaitement bien.
Combinez cela avec la solution de Jules, et cela devrait bien fonctionner. Peut-être aussi rechercher des fréquences de mots-clés pour un point supplémentaire.
Intéressant. J'ai une tâche similaire pour reconnaître du texte dans différents formats. Propriétés YAML, JSON, XML ou Java? Même avec des erreurs de syntaxe, par exemple, je devrais distinguer JSON de XML avec confiance.
Je pense que la façon dont nous modélisons le problème est essentielle. Comme Mark l'a dit, la tokenisation d'un seul mot est nécessaire mais probablement pas suffisante. Nous aurons besoin de bigrammes, voire de trigrammes. Mais je pense qu'on peut aller plus loin à partir de là en sachant que l'on s'intéresse aux langages de programmation. Je remarque que presque tous les langages de programmation ont deux types uniques de jetons - symboles et mots - clés . Les symboles sont relativement faciles (certains symboles peuvent être des littéraux ne faisant pas partie du langage) à reconnaître. Ensuite, les bigrammes ou trigrammes de symboles prendront des structures de syntaxe uniques autour des symboles. Les mots clés sont une autre cible facile si l'ensemble de formation est suffisamment vaste et diversifié. Une fonctionnalité utile pourrait être des bigrammes autour de mots clés possibles. Un autre type de jeton intéressant est l' espace blanc. En fait, si nous tokenisons de la manière habituelle par un espace blanc, nous perdrons cette information. Je dirais que pour analyser les langages de programmation, nous conservons les jetons d'espacement car cela peut contenir des informations utiles sur la structure de la syntaxe.
Enfin, si je choisis un classificateur comme la forêt aléatoire, je vais explorer github et rassembler tout le code source public. La plupart des fichiers de code source peuvent être étiquetés par suffixe de fichier. Pour chaque fichier, je le diviserai au hasard en lignes vides en extraits de différentes tailles. Je vais ensuite extraire les fonctionnalités et entraîner le classificateur en utilisant les extraits étiquetés. Une fois la formation terminée, le classificateur peut être testé pour la précision et le rappel.
La meilleure solution que j'ai rencontrée est d'utiliser le joyau du linguiste dans une application Ruby on Rails. C'est en quelque sorte une manière spécifique de le faire, mais cela fonctionne. Cela a été mentionné ci-dessus par @nisc mais je vais vous dire mes étapes exactes pour l'utiliser. (Certaines des commandes de ligne de commande suivantes sont spécifiques à ubuntu mais devraient être facilement traduites vers d'autres systèmes d'exploitation)
Si vous avez une application de rails qui ne vous dérange pas de jouer temporairement, créez un nouveau fichier dedans pour insérer votre extrait de code en question. (Si vous ne disposez pas des rails installés il y a un bon guide ici bien pour ubuntu Je recommande ce . Ensuite , exécutez rails new <name-your-app-dir>
- vous dans ce répertoire. Tout ce que vous devez exécuter une application des rails est déjà là).
Une fois que vous avez une application de rails avec laquelle l'utiliser, ajoutez-la gem 'github-linguist'
à votre Gemfile (littéralement juste appelée Gemfile
dans le répertoire de votre application, pas d'ext).
Ensuite, installez ruby-dev ( sudo apt-get install ruby-dev
)
Puis installez cmake ( sudo apt-get install cmake
)
Maintenant, vous pouvez exécuter gem install github-linguist
(si vous obtenez une erreur indiquant que icu est requis, faites sudo apt-get install libicu-dev
et réessayez)
(Vous devrez peut-être faire un sudo apt-get update
ou sudo apt-get install make
ou sudo apt-get install build-essential
si ce qui précède n'a pas fonctionné)
Maintenant, tout est mis en place. Vous pouvez désormais l'utiliser à tout moment pour vérifier des extraits de code. Dans un éditeur de texte, ouvrez le fichier que vous avez créé pour insérer votre extrait de code (disons simplement que c'est, app/test.tpl
mais si vous connaissez l'extension de votre extrait, utilisez-la à la place de .tpl
. Si vous ne connaissez pas l'extension, n'en utilisez pas une ). Collez maintenant votre extrait de code dans ce fichier. Accédez à la ligne de commande et exécutez bundle install
(doit être dans le répertoire de votre application). Puis exécutez linguist app/test.tpl
(plus généralement linguist <path-to-code-snippet-file>
). Il vous indiquera le type, le type mime et la langue. Pour plusieurs fichiers (ou pour une utilisation générale avec une application ruby / rails), vous pouvez exécuter bundle exec linguist --breakdown
dans le répertoire de votre application.
Cela semble demander beaucoup de travail supplémentaire, surtout si vous n'avez pas déjà de rails, mais vous n'avez en fait besoin de rien savoir sur les rails si vous suivez ces étapes et que je n'ai vraiment pas trouvé de meilleur moyen de détecter le langue d'un fichier / extrait de code.
Je crois qu'il n'y a pas de solution unique qui pourrait éventuellement identifier la langue d'un extrait de code, juste en fonction de cet extrait de code unique. Prenez le mot-clé print
. Il peut apparaître dans n'importe quel nombre de langues, dont chacune est à des fins différentes, et avoir une syntaxe différente.
J'ai quelques conseils. J'écris actuellement un petit morceau de code pour mon site Web qui peut être utilisé pour identifier les langages de programmation. Comme la plupart des autres articles, il pourrait y avoir une vaste gamme de langages de programmation que vous n'avez tout simplement pas entendus, vous ne pouvez pas tous les expliquer.
Ce que j'ai fait, c'est que chaque langue peut être identifiée par une sélection de mots-clés. Par exemple, Python peut être identifié de plusieurs manières. C'est probablement plus facile si vous choisissez des «traits» qui sont certainement propres à la langue. Pour Python, je choisis le trait d'utiliser deux points pour démarrer un ensemble d'instructions, ce qui, je crois, est un trait assez unique (corrigez-moi si je me trompe).
Si, dans mon exemple, vous ne trouvez pas de deux-points pour démarrer un ensemble d'instructions, passez à un autre trait possible, disons en utilisant le def
mot - clé pour définir une fonction. Maintenant, cela peut poser des problèmes, car Ruby utilise également le mot-clé def
pour définir une fonction. La clé pour distinguer les deux (Python et Ruby) est d'utiliser différents niveaux de filtrage pour obtenir la meilleure correspondance. Ruby utilise le mot-clé end
pour terminer une fonction, alors que Python n'a rien pour terminer une fonction, juste un retrait mais vous ne voulez pas y aller. Mais encore une fois, end
pourrait aussi être Lua, encore un autre langage de programmation à ajouter au mélange.
Vous pouvez voir que les langages de programmation se superposent simplement trop. Un mot-clé qui pourrait être un mot-clé dans une langue pourrait se trouver être un mot-clé dans une autre langue. L'utilisation d'une combinaison de mots-clés qui vont souvent ensemble, comme ceux de Java, public static void main(String[] args)
aide à éliminer ces problèmes.
Comme je l'ai déjà dit, votre meilleure chance est de rechercher des mots-clés relativement uniques ou des ensembles de mots-clés pour séparer l'un de l'autre. Et, si vous vous trompez, au moins vous avez essayé.
Ce site semble être assez bon pour identifier les langues, si vous voulez un moyen rapide de coller un extrait dans un formulaire Web, plutôt que de le faire par programme: http://dpaste.com/