¿Cuál sería la mejor manera de detectar qué lenguaje de programación se usa en un fragmento de código?
¿Cuál sería la mejor manera de detectar qué lenguaje de programación se usa en un fragmento de código?
Respuestas:
Creo que el método utilizado en los filtros de spam funcionaría muy bien. Divides el fragmento en palabras. Luego, compara las ocurrencias de estas palabras con fragmentos conocidos y calcula la probabilidad de que este fragmento esté escrito en el idioma X para todos los idiomas que le interesan.
http://en.wikipedia.org/wiki/Bayesian_spam_filtering
Si tiene el mecanismo básico, entonces es muy fácil agregar nuevos idiomas: simplemente entrene el detector con algunos fragmentos en el nuevo idioma (podría alimentarlo con un proyecto de código abierto). De esta manera, aprende que es probable que "Sistema" aparezca en los fragmentos de C # y "pone" en los fragmentos de Ruby.
De hecho, he usado este método para agregar detección de idioma a fragmentos de código para el software del foro. Funcionó el 100% del tiempo, excepto en casos ambiguos:
print "Hello"
Déjame encontrar el código.
No pude encontrar el código, así que hice uno nuevo. Es un poco simplista pero funciona para mis pruebas. Actualmente, si lo alimenta con mucho más código Python que código Ruby, es probable que diga que este código:
def foo
puts "hi"
end
es código Python (aunque realmente es Ruby). Esto se debe a que Python también tiene una def
palabra clave. Entonces, si ha visto 1000x def
en Python y 100x def
en Ruby, aún puede decir Python aunque puts
yend
es Rubí-específica. Puede solucionar esto haciendo un seguimiento de las palabras que se ven por idioma y dividiéndolas por eso en algún lugar (o alimentándolo con cantidades iguales de código en cada idioma).
Espero que te ayude:
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)
$
, por lo que tal vez no debería dividir en límites de palabras, porque $
debería quedarse con la variable. A los operadores les gusta =>
y :=
deberían estar unidos como un solo token, pero OTH probablemente debería dividirlos {
porque siempre se mantienen solos.
Detección de idioma resuelta por otros:
Enfoque de Ohloh: https://github.com/blackducksw/ohcount/
Enfoque de Github: https://github.com/github/linguist
Puede encontrar material útil aquí: http://alexgorbatchev.com/wiki/SyntaxHighlighter . Alex ha dedicado mucho tiempo a averiguar cómo analizar una gran cantidad de idiomas diferentes y cuáles son los elementos de sintaxis clave.
Guesslang es una posible solución:
http://guesslang.readthedocs.io/en/latest/index.html
También hay SourceClassifier:
https://github.com/chrislo/sourceclassifier/tree/master
Me interesé en este problema después de encontrar un código en un artículo de blog que no pude identificar. Añadiendo esta respuesta, ya que esta pregunta fue el primer resultado de búsqueda para "identificar lenguaje de programación".
Es muy difícil y a veces imposible. ¿De qué idioma es este breve fragmento?
int i = 5;
int k = 0;
for (int j = 100 ; j > i ; i++) {
j = j + 1000 / i;
k = k + i * j;
}
(Pista: podría ser cualquiera de varios).
Puede intentar analizar varios idiomas e intentar decidir mediante el análisis de frecuencia de palabras clave. Si ciertos conjuntos de palabras clave ocurren con ciertas frecuencias en un texto, es probable que el lenguaje sea Java, etc. Pero no creo que obtenga nada que sea completamente infalible, ya que podría nombrar, por ejemplo, una variable en C con el mismo nombre como palabra clave en Java, y el análisis de frecuencia será engañado.
Si lo lleva a un nivel superior en complejidad, podría buscar estructuras, si una determinada palabra clave siempre viene después de otra, eso le dará más pistas. Pero también será mucho más difícil de diseñar e implementar.
Una alternativa es usar highlight.js , que realiza el resaltado de sintaxis pero usa la tasa de éxito del proceso de resaltado para identificar el idioma. En principio, cualquier base de código de resaltador de sintaxis podría usarse de la misma manera, pero lo bueno de highlight.js es que la detección de idioma se considera una característica y se usa con fines de prueba .
ACTUALIZACIÓN: Intenté esto y no funcionó tan bien. JavaScript comprimido lo confundió por completo, es decir, el tokenizador es sensible a los espacios en blanco. En general, el simple hecho de contar los resultados destacados no parece muy confiable. Un analizador más sólido, o quizás recuentos de secciones incomparables, podrían funcionar mejor.
Primero, trataría de encontrar las teclas específicas de un idioma, por ejemplo
"package, class, implements "=> JAVA
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...
Dependería del tipo de fragmento que tenga, pero lo ejecutaría a través de una serie de tokenizadores y vería el BNF de qué idioma se encontró como válido.
Buen rompecabezas.
Creo que es imposible detectar todos los idiomas. Pero puedes disparar con tokens clave. (ciertas palabras reservadas y combinaciones de caracteres de uso frecuente).
Ben, hay muchos lenguajes con sintaxis similar. Por tanto, depende del tamaño del fragmento.
Prettify es un paquete de Javascript que hace un buen trabajo al detectar lenguajes de programación:
http://code.google.com/p/google-code-prettify/
Es principalmente un resaltador de sintaxis, pero probablemente haya una manera de extraer la parte de detección con el fin de detectar el idioma de un fragmento.
Necesitaba esto, así que creé el mío. https://github.com/bertyhell/CodeClassifier
Es muy fácil de ampliar agregando un archivo de entrenamiento en la carpeta correcta. Escrito en c #. Pero imagino que el código se convierte fácilmente a cualquier otro idioma.
No creo que haya una manera fácil de lograrlo. Probablemente generaría listas de símbolos / palabras clave comunes únicas para ciertos idiomas / clases de idiomas (por ejemplo, corchetes para lenguaje de estilo C, las palabras clave Dim y Sub para lenguajes BASIC, la palabra clave def para Python, la palabra clave let para lenguajes funcionales) . A continuación, es posible que pueda utilizar funciones de sintaxis básicas para reducirlo aún más.
Creo que la mayor distinción entre idiomas es su estructura. Entonces mi idea sería mirar ciertos elementos comunes en todos los idiomas y ver en qué se diferencian. Por ejemplo, puede usar expresiones regulares para seleccionar cosas como:
Y tal vez algunas otras cosas que la mayoría de los idiomas deberían tener. Luego usa un sistema de puntos. Otorgue como máximo 1 punto por cada elemento si se encuentra la expresión regular. Obviamente, algunos lenguajes usarán exactamente la misma sintaxis (los bucles for a menudo se escriben comofor(int i=0; i<x; ++i)
por lo que varios idiomas podrían obtener un punto por lo mismo, pero al menos está reduciendo la probabilidad de que sea un idioma completamente diferente). Algunos de ellos pueden puntuar 0 en todos los ámbitos (el fragmento no contiene ninguna función, por ejemplo), pero eso está perfectamente bien.
Combine esto con la solución de Jules, y debería funcionar bastante bien. Quizás también busque frecuencias de palabras clave para obtener un punto extra.
Interesante. Tengo una tarea similar para reconocer texto en diferentes formatos. ¿Propiedades YAML, JSON, XML o Java? Incluso con errores de sintaxis, por ejemplo, debería distinguir JSON de XML con confianza.
Me imagino que cómo modelamos el problema es fundamental. Como dijo Mark, la tokenización de una sola palabra es necesaria, pero probablemente no sea suficiente. Necesitaremos bigramas o incluso trigramas. Pero creo que podemos ir más lejos sabiendo que estamos viendo lenguajes de programación. Noto que casi cualquier lenguaje de programación tiene dos tipos únicos de tokens: símbolos y palabras clave . Los símbolos son relativamente fáciles de reconocer (algunos símbolos pueden ser literales que no forman parte del idioma). Entonces, los bigramas o trigramas de símbolos recogerán estructuras de sintaxis únicas alrededor de los símbolos. Las palabras clave son otro objetivo fácil si el conjunto de formación es lo suficientemente grande y diverso. Una característica útil podría ser bigramas en torno a posibles palabras clave. Otro tipo interesante de token es espacio en blanco.. En realidad, si tokenizamos de la forma habitual mediante espacios en blanco, perderemos esta información. Yo diría que, para analizar lenguajes de programación, mantenemos los tokens de espacios en blanco, ya que pueden contener información útil sobre la estructura de sintaxis.
Finalmente, si elijo un clasificador como bosque aleatorio, rastrearé github y reuniré todo el código fuente público. La mayor parte del archivo de código fuente se puede etiquetar por sufijo de archivo. Para cada archivo, lo dividiré aleatoriamente en líneas vacías en fragmentos de varios tamaños. Luego extraeré las características y entrenaré al clasificador usando los fragmentos etiquetados. Una vez finalizado el entrenamiento, se puede probar la precisión y la recuperación del clasificador.
La mejor solución que he encontrado es usar la gema lingüista en una aplicación Ruby on Rails. Es una forma específica de hacerlo, pero funciona. Esto fue mencionado anteriormente por @nisc, pero te diré mis pasos exactos para usarlo. (Algunos de los siguientes comandos de línea de comandos son específicos de ubuntu, pero deberían traducirse fácilmente a otros sistemas operativos)
Si tiene alguna aplicación de rails con la que no le importa jugar temporalmente, cree un nuevo archivo para insertar el fragmento de código en cuestión. (Si no tiene rieles instalados, hay una buena guía aquí, aunque para ubuntu recomiendo esto . Luego ejecute rails new <name-your-app-dir>
y cd en ese directorio. Todo lo que necesita para ejecutar una aplicación de rieles ya está allí).
Después de tener una aplicación de rieles para usarla, agréguela gem 'github-linguist'
a su Gemfile (literalmente, se acaba de llamar Gemfile
en el directorio de su aplicación, no ext).
Luego instale ruby-dev ( sudo apt-get install ruby-dev
)
Luego instale cmake ( sudo apt-get install cmake
)
Ahora puede ejecutar gem install github-linguist
(si recibe un error que dice que se requiere icu, hágalo sudo apt-get install libicu-dev
e intente nuevamente)
(Es posible que tenga que hacer una sudo apt-get update
o sudo apt-get install make
o sudo apt-get install build-essential
si lo anterior no funcionó)
Ahora todo está configurado. Ahora puede usar esto en cualquier momento que desee verificar fragmentos de código. En un editor de texto, abra el archivo que creó para insertar su fragmento de código (digamos que es, app/test.tpl
pero si conoce la extensión de su fragmento, utilícelo en lugar de .tpl
. Si no conoce la extensión, no use una ). Ahora pegue su fragmento de código en este archivo. Vaya a la línea de comandos y ejecute bundle install
(debe estar en el directorio de su aplicación). Luego ejecute linguist app/test.tpl
(de manera más general linguist <path-to-code-snippet-file>
). Le dirá el tipo, el tipo de mímica y el idioma. Para varios archivos (o para uso general con una aplicación ruby / rails) puede ejecutar bundle exec linguist --breakdown
en el directorio de su aplicación.
Parece mucho trabajo extra, especialmente si aún no tiene rieles, pero en realidad no necesita saber NADA sobre rieles si sigue estos pasos y realmente no he encontrado una mejor manera de detectar el idioma de un archivo / fragmento de código.
Creo que no existe una solución única que pueda identificar en qué idioma se encuentra un fragmento, solo basándose en ese único fragmento. Toma la palabra clave print
. Puede aparecer en cualquier número de idiomas, cada uno de los cuales tiene diferentes propósitos y tiene una sintaxis diferente.
Tengo algunos consejos. Actualmente estoy escribiendo un pequeño código para mi sitio web que se puede usar para identificar lenguajes de programación. Como la mayoría de las otras publicaciones, podría haber una gran variedad de lenguajes de programación que simplemente no ha escuchado, no puede explicarlos todos.
Lo que he hecho es que cada idioma se puede identificar mediante una selección de palabras clave. Por ejemplo, Python se puede identificar de varias formas. Probablemente sea más fácil si eliges 'rasgos' que también son ciertamente exclusivos del idioma. Para Python, elijo el rasgo de usar dos puntos para iniciar un conjunto de declaraciones, que creo que es un rasgo bastante único (corríjame si me equivoco).
Si, en mi ejemplo, no puede encontrar dos puntos para iniciar un conjunto de instrucciones, luego pase a otro rasgo posible, digamos que usa la def
palabra clave para definir una función. Ahora bien, esto puede causar algunos problemas, porque Ruby también usa la palabra clave def
para definir una función. La clave para diferenciar los dos (Python y Ruby) es usar varios niveles de filtrado para obtener la mejor coincidencia. Ruby usa la palabra clave end
para terminar una función, mientras que Python no tiene nada para terminar una función, solo un desangrado pero no quieres ir allí. Pero nuevamente, end
también podría ser Lua, otro lenguaje de programación más para agregar a la mezcla.
Puede ver que los lenguajes de programación simplemente se superponen demasiado. Una palabra clave que podría ser una palabra clave en un idioma podría ser una palabra clave en otro idioma. El uso de una combinación de palabras clave que a menudo van juntas, como Java, public static void main(String[] args)
ayuda a eliminar esos problemas.
Como ya he dicho, su mejor oportunidad es buscar palabras clave relativamente únicas o conjuntos de palabras clave para separar una de la otra. Y, si te equivocas, al menos lo intentaste.
Este sitio parece ser bastante bueno para identificar idiomas, si desea una forma rápida de pegar un fragmento en un formulario web, en lugar de hacerlo mediante programación: http://dpaste.com/