Quel serait le meilleur moyen de détecter le langage de programmation utilisé dans un extrait de code?
Je pense que la méthode utilisée dans les filtres anti-spam fonctionnerait très bien. Vous divisez le fragment en mots. Vous comparez ensuite les occurrences de ces mots avec des extraits connus et calculez la probabilité que cet extrait soit écrit en langue X pour toutes les langues qui vous intéressent.
http://en.wikipedia.org/wiki/Bayesian_spam_filtering
Si vous avez le mécanisme de base, 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 avec un projet open source). De cette façon, il apprend que "Système" est susceptible d'apparaître dans les extraits C # et "met" dans les extraits Ruby.
J'ai en fait utilisé cette méthode pour ajouter la détection de langue aux extraits de code des logiciels de forum. Cela a fonctionné 100% du temps, sauf dans les cas ambigus:
print "Hello"
Laisse moi trouver le code.
Je n'ai pas pu trouver le code alors j'en ai créé un nouveau. C'est un peu simpliste mais cela fonctionne pour mes tests. Actuellement, si vous alimentez beaucoup plus de code Python que Ruby, il est probable que ce code:
def foo
puts "hi"
end
est du code Python (bien que ce soit vraiment Ruby). En effet, Python a aussi un mot clé def
. 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
sont spécifiques à Ruby. Vous pouvez résoudre ce problème en gardant une trace des mots vus par langue et en les divisant 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)
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 pouvez trouver du matériel utile ici: http://alexgorbatchev.com/wiki/SyntaxHighlighter . Alex a passé beaucoup de temps à chercher comment analyser un grand nombre de langues différentes et quelle en était la clé. les éléments de syntaxe sont.
Une alternative consiste à utiliser highlight.js , qui effectue la coloration syntaxique mais utilise le taux de réussite du processus de coloration pour identifier la langue. En principe, toute syntaxe de surligneur en syntaxe pourrait être utilisée de la même manière, mais l’avantage de highlight.js est que la détection de langue est considérée comme une fonctionnalité et est utilisée à des fins de test .
UPDATE: J'ai essayé cela et cela n'a pas très bien fonctionné. Le code JavaScript compressé l’a complètement confondu, c’est-à-dire que le tokenizer est sensible aux espaces. De manière générale, le simple fait de compter les hits ne semble pas très fiable. Un analyseur syntaxique plus puissant, ou peut-être des comptages de sections incomparables, pourrait fonctionner mieux.
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 pourrait s'agir de n'importe lequel parmi plusieurs.)
Vous pouvez essayer d'analyser différentes langues et décider d'utiliser une analyse de fréquence des mots clés. Si certains ensembles de mots-clés apparaissent avec certaines fréquences dans un texte, il est probable que le langage utilisé est Java, etc. en tant que mot-clé en Java, et l'analyse de fréquence sera trompée.
Si vous prenez un peu de complexité, vous pouvez rechercher des structures. Si un 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.
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 ne pouvais pas identifier. Ajouter cette réponse puisque cette question était le premier résultat de recherche pour "identifier le langage de programmation".
Tout d’abord, j’essaierais de trouver les clés spécifiques d’une langue, par exemple.
"package, class, implements "=> Java
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...
Cela dépend du type d'extrait que vous avez, mais je l'examinerais à travers une série de tokenizers pour voir dans quelle langue le BNF serait utilisé.
J'avais besoin de ça alors j'ai créé mon propre . https://github.com/bertyhell/CodeClassifier
Il est très facilement extensible en ajoutant un fichier d’entraînement au bon dossier . Écrit en c #. Mais j'imagine que le code est facilement converti en une autre langue.
Joli puzzle.
Je pense qu'il est impossible de détecter toutes les langues. Mais vous pouvez déclencher des jetons clés. (certains mots réservés et combinaisons de caractères souvent utilisées).
Ben il y a beaucoup de langues avec une syntaxe similaire. Cela dépend donc de la taille de l'extrait.
Prettify est un paquet Javascript qui détecte bien 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 détection dans le but de détecter la langue à partir d'un extrait.
Configurez le brouilleur aléatoire comme
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;
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 rationnelles pour choisir 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 la regex est trouvée. De toute évidence, certaines langues utiliseront exactement la même syntaxe (les boucles étant souvent écrites comme for(int i=0; i<x; ++i)
, plusieurs langues pourraient marquer un point pour la même chose, mais au moins, vous réduisez le risque que ce soit une langue totalement différente). Certains d'entre eux peuvent marquer des 0 dans tous les domaines (le fragment ne contient pas de fonction du tout, par exemple), mais c'est parfaitement correct.
Combinez cela avec la solution de Jules et cela devrait fonctionner assez bien. Peut-être aussi rechercher des fréquences de mots-clés pour un point supplémentaire.
La meilleure solution que j'ai rencontrée consiste à utiliser le linguist gem dans une application Ruby on Rails. C'est un peu une façon spécifique de le faire, mais ça marche. Ceci a été mentionné ci-dessus par @nisc mais je vais vous dire les étapes à suivre pour l’utiliser. (Certaines des commandes de ligne de commande suivantes sont spécifiques à ubuntu mais doivent être facilement traduites vers d'autres systèmes d'exploitation)
Si vous avez une application Rails que vous ne voulez pas déranger temporairement, créez-y un nouveau fichier pour y insérer l'extrait de code en question. (Si Rails n'est pas installé, il existe un bon guide ici bien que pour Ubuntu, je recommande ce . Ensuite, exécutez Rails new <name-your-app-dir>
et cd dans ce répertoire. Tout ce dont vous avez besoin pour exécuter une application Rails existe déjà) .
Une fois que vous avez une application Rails avec laquelle utiliser cela, ajoutez gem 'github-linguist'
à votre Gemfile (littéralement appelé juste Gemfile
dans votre répertoire d'application, sans extension).
Puis 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 disant que icu est requis, faites Sudo apt-get install libicu-dev
et essayez à nouveau)
(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 maintenant l'utiliser à tout moment pour vérifier les 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-le plutôt que .tpl
. Si vous ne connaissez pas l'extension, ne le faites pas. utilisez-en un). Collez maintenant votre extrait de code dans ce fichier. Accédez à la ligne de commande et exécutez bundle install
(doit figurer 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 de 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.
Il semble que cela demande beaucoup de travail supplémentaire, surtout si vous n’avez pas encore Rails, mais vous n’avez vraiment pas besoin de savoir quoi que ce soit à propos de Rails si vous suivez ces étapes et je n’ai vraiment pas trouvé le meilleur moyen de détecter les langue d'un extrait de fichier/code.
Je crois qu’il n’existe pas de solution unique permettant d’identifier la langue dans laquelle se trouve un extrait de code, mais uniquement sur la base de cet extrait de code. Prenez le mot-clé print
. Il peut apparaître dans un grand nombre de langues, chacune ayant des objectifs différents et 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 un grand nombre de langages de programmation énormes que vous n'avez tout simplement pas entendus.
Ce que j'ai fait est que chaque langue peut être identifiée par une sélection de mots-clés. Par exemple, Python pourrait être identifié de différentes manières. C'est probablement plus facile si vous choisissez des «traits» qui sont également uniques à la langue. Pour Python, j’ai choisi le trait de caractère d’utiliser des deux points pour commencer un ensemble d’énoncés, ce qui, à mon avis, est un trait assez unique (corrigez-moi si je me trompe).
Si, dans mon exemple, vous ne trouvez pas un colon pour démarrer un jeu d'instructions, passez à un autre trait possible, par exemple, utilisez le mot clé def
pour définir une fonction. Cela peut maintenant poser problème, car Ruby utilise également le mot clé def
pour définir une fonction. La clé pour distinguer les deux (Python et Ruby) consiste à 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 tout simplement trop. Un mot clé qui pourrait être un mot clé dans une langue pourrait être un mot clé dans une autre langue. L'utilisation d'une combinaison de mots clés qui vont souvent ensemble, comme la fonction public static void main(String[] args)
de Java, permet d'é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 les séparer les uns des autres. Et, si vous vous trompez, au moins vous essayez.
Je ne pensais pas qu'il y aurait un moyen facile d'accomplir cela. Je générerais probablement des listes de symboles/mots clés communs propres à certaines langues/classes de langues (par exemple, des 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.
Intéressant. J'ai une tâche similaire à reconnaître le 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 en toute confiance.
Je pense que la façon dont nous modélisons le problème est critique. Comme Mark l'a dit, la création d'un seul mot par mot est nécessaire mais probablement pas suffisante. Nous aurons besoin de bigrames, voire de trigrammes. Mais je pense que nous pouvons aller plus loin à partir de là en sachant que nous examinons les 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 à reconnaître (certains symboles peuvent être des littéraux ne faisant pas partie du langage). Ensuite, les bigrammes ou les trigrammes de symboles choisiront des structures de syntaxe uniques autour des symboles. Les mots-clés sont une autre cible facile si l'ensemble de formation est vaste et suffisamment diversifié. Une fonctionnalité utile pourrait être bigrams autour des mots-clés possibles. Un autre type de jeton intéressant est espace blanc . En fait, si nous identifions comme d'habitude par des espaces, nous perdrons cette information. Je dirais que pour l'analyse des langages de programmation, nous conservons les jetons d'espaces, car ils peuvent contenir des informations utiles sur la structure de la syntaxe.
Enfin, si je choisis un classificateur comme 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 séparerai au hasard sur des lignes vides en extraits de différentes tailles. Je vais ensuite extraire les caractéristiques et former 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.