Каким будет лучший способ определить, какой язык программирования используется во фрагменте кода?
Обнаружение языка программирования из фрагмента
Ответ 1
Я думаю, что метод, используемый в спам-фильтрах, будет работать очень хорошо. Вы разделили фрагмент на слова. Затем вы сравниваете вхождения этих слов с известными фрагментами и вычисляете вероятность того, что этот фрагмент написан на языке X для каждого интересующего вас языка.
http://en.wikipedia.org/wiki/Bayesian_spam_filtering
Если у вас есть основной механизм, то очень легко добавить новые языки: просто обучите детектор несколькими фрагментами на новом языке (вы можете подать проект с открытым исходным кодом). Таким образом, он узнает, что "система" скорее всего появится в фрагментах С# и "помещает" в фрагменты Ruby.
Я действительно использовал этот метод для добавления определения языка в фрагменты кода для программного обеспечения форума. Он работал в 100% случаев, за исключением двусмысленных случаев:
print "Hello"
Позвольте мне найти код.
Я не смог найти код, поэтому я создал новый. Он немного упрощен, но он работает для моих тестов. В настоящее время, если вы кормите его гораздо больше кода Python, чем код Ruby, он может сказать, что этот код:
def foo
puts "hi"
end
- это код Python (хотя это действительно Ruby). Это связано с тем, что Python имеет ключевое слово def
. Итак, если он видел 1000x def
в Python и 100x def
в Ruby, он все равно может сказать Python, хотя puts
и end
является специфичным для Ruby. Вы можете это исправить, отслеживая слова, видимые на одном языке, и разделяете их где-нибудь (или путем подачи одинакового количества кода на каждом языке).
Надеюсь, это поможет вам:
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)
Ответ 2
Определение языка решается другими:
подход Ohloh: https://github.com/blackducksw/ohcount/
Подход Github: https://github.com/github/linguist
Ответ 3
Здесь вы можете найти полезный материал: http://alexgorbatchev.com/wiki/SyntaxHighlighter. Алекс потратил много времени на выяснение того, как разбирать большое количество разных языков и каковы ключевые синтаксические элементы.
Ответ 4
Это очень сложно, а иногда и невозможно. На каком языке этот короткий фрагмент из?
int i = 5;
int k = 0;
for (int j = 100 ; j > i ; i++) {
j = j + 1000 / i;
k = k + i * j;
}
(Подсказка: это может быть любой из нескольких.)
Вы можете попробовать проанализировать различные языки и попытаться решить частотный анализ ключевых слов. Если определенные группы ключевых слов встречаются с определенными частотами в тексте, вероятно, что язык - это Java и т.д. Но я не думаю, что вы получите что-либо, что полностью доказано дураком, как вы могли бы назвать, например, переменную с C с тем же именем как ключевое слово в Java, и анализ частоты будет обманут.
Если вы займетесь сложностью, вы можете искать структуры, если определенное ключевое слово всегда приходит после другого, что даст вам больше подсказок. Но также будет сложнее проектировать и реализовывать.
Ответ 5
Альтернативой является использование highlight.js, который выполняет подсветку синтаксиса, но использует скорость успеха процесса выделения для идентификации языка. В принципе, любой синтаксический ярлык codebase можно было бы использовать одинаково, но приятная вещь о highlight.js заключается в том, что распознавание языка считается особенностью и используется для целей тестирования.
ОБНОВЛЕНИЕ: Я пробовал это, и он не работал так хорошо. Сжатый JavaScript полностью смутил его, т.е. Токенизатор чувствителен к пробелу. Как правило, просто подсчет бликов не кажется очень надежным. Более сильный синтаксический анализатор, или, возможно, непревзойденное количество разделов, может работать лучше.
Ответ 6
Сначала я попытался бы найти конкретные ключевые слова языка, например.
"package, class, implements "=> JAVA
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...
Ответ 7
Это будет зависеть от того, какой тип фрагмента у вас есть, но я буду запускать его через ряд токенизаторов и посмотреть, какой язык BNF он выбрал как действительный.
Ответ 8
Хорошая головоломка.
Я думаю, что невозможно обнаружить все языки. Но вы можете запускать ключевые токены. (некоторые зарезервированные слова и часто используемые комбинации символов).
В Бен много языков с похожим синтаксисом. Это зависит от размера фрагмента.
Ответ 9
Prettify - это Javascript-пакет, который хорошо подходит для обнаружения языков программирования:
http://code.google.com/p/google-code-prettify/
Это в основном синтаксический ярлык, но, вероятно, есть способ извлечь часть обнаружения для обнаружения языка из фрагмента.
Ответ 10
Мне это нужно, поэтому я создал свой собственный. https://github.com/bertyhell/CodeClassifier
Он очень легко расширяется, добавляя учебный файл в правильную папку. Написано в С#. Но я представляю, что код легко конвертируется на любой другой язык.
Ответ 11
Я бы не подумал, что будет простой способ сделать это. Я бы, вероятно, создал списки символов/общих ключевых слов, уникальных для определенных языков/классов языков (например, фигурные скобки для языка C-стиля, слова Dim и Sub для базовых языков, ключевое слово def для Python, ключевое слово let для функциональных языков), Затем вы сможете использовать основные синтаксические функции, чтобы еще больше сузить его.
Ответ 12
Я думаю, что самым большим различием между языками является его структура. Поэтому я хотел бы взглянуть на некоторые общие элементы на всех языках и посмотреть, как они отличаются. Например, вы можете использовать регулярные выражения для выбора таких вещей, как:
- определения функций
- объявления переменных
- объявления класса
- комментарии
- для циклов
- while while
- распечатать заявления
И, возможно, несколько других вещей, которые должны иметь большинство языков. Затем используйте точечную систему. Награда не более 1 балла за каждый элемент, если найдено регулярное выражение. Очевидно, что некоторые языки будут использовать один и тот же синтаксис (поскольку циклы часто пишутся как for(int i=0; i<x; ++i)
, поэтому несколько языков могут оценивать точку за одно и то же, но по крайней мере вы уменьшаете вероятность того, что это совершенно другой язык), Некоторые из них могут оценивать 0s по всей доске (например, фрагмент не содержит функции вообще), но это прекрасно.
Объедините это с решением Jules, и оно должно работать очень хорошо. Возможно, также ищите частоты ключевых слов для дополнительной точки.
Ответ 13
Интересно. У меня аналогичная задача - распознавать текст в разных форматах. YAML, JSON, XML или Java? Даже с синтаксическими ошибками, например, я должен с уверенностью отделить JSON от XML.
Я понимаю, как мы моделируем проблему. Как сказал Марк, однократная токенизация необходима, но, скорее всего, недостаточно. Нам понадобятся битрамы или даже триграммы. Но я думаю, что мы можем идти дальше оттуда, зная, что мы смотрим на языки программирования. Я заметил, что почти любой язык программирования имеет два уникальных типа токенов - символы и ключевые слова. Символы относительно легкие (некоторые символы могут быть литералами, не являющимися частью языка) для распознавания. Тогда биграммы или триграммы символов будут отображать уникальные синтаксические структуры вокруг символов. Ключевые слова - еще одна легкая цель, если набор упражнений является большим и достаточно разнообразным. Полезной особенностью может быть bigrams вокруг возможных ключевых слов. Еще один интересный тип токена - whitespace. На самом деле, если мы будем обычным образом обозначать пробел, мы потеряем эту информацию. Я бы сказал, для анализа языков программирования мы сохраняем символы прокрутки, так как это может нести полезную информацию о структуре синтаксиса.
Наконец, если я выберу классификатор, например, случайный лес, я буду обходить github и собрать все общедоступные исходные коды. Большая часть файла исходного кода может быть помечена суффиксом файла. Для каждого файла я буду беспорядочно разбивать его на пустые строки на фрагменты разных размеров. Затем я извлечу функции и обучу классификатор с помощью помеченных фрагментов. После завершения обучения классификатор может быть проверен на точность и отзыв.
Ответ 14
Лучшее решение, с которым я столкнулся, использует лингвистический камень в приложении Ruby on Rails. Это своего рода конкретный способ сделать это, но он работает. Это было упомянуто выше @nisc, но я расскажу вам свои точные шаги для его использования. (Некоторые из следующих команд командной строки специфичны для ubuntu, но их следует легко перевести на другие ОС)
Если у вас есть приложение rails, с которым вы не возражаете временно возиться, создайте в нем новый файл, чтобы вставить ваш фрагмент кода. (Если у вас нет рельсов, там есть хороший путеводитель здесь, хотя для ubuntu я рекомендую this. Затем запустите rails new <name-your-app-dir>
и cd в этот каталог. Все, что вам нужно для запуска приложения rails, уже существует).
После того, как у вас есть приложение rails для использования этого, добавьте gem 'github-linguist'
в свой Gemfile (буквально только что называемый Gemfile
в вашем каталоге приложений, no ext).
Затем установите ruby-dev (sudo apt-get install ruby-dev
)
Затем установите cmake (sudo apt-get install cmake
)
Теперь вы можете запустить gem install github-linguist
(если вы получите сообщение об ошибке, требуемое icu, выполните sudo apt-get install libicu-dev
и повторите попытку)
(Возможно, вам понадобится сделать sudo apt-get update
или sudo apt-get install make
или sudo apt-get install build-essential
, если выше не работает)
Теперь все настроено. Теперь вы можете использовать это в любое время, когда хотите проверить фрагменты кода. В текстовом редакторе откройте файл, который вы сделали, чтобы вставить фрагмент кода (пусть он просто скажет app/test.tpl
, но если вы знаете расширение своего фрагмента, используйте его вместо .tpl
). Если вы не знаете расширение, не используйте один). Теперь вставьте фрагмент кода в этот файл. Перейдите в командную строку и запустите bundle install
(должно быть в вашем каталоге приложения). Затем запустите linguist app/test.tpl
(в общем случае linguist <path-to-code-snippet-file>
). Он расскажет вам тип, тип mime и язык. Для нескольких файлов (или для общего использования с приложением ruby /rails) вы можете запустить bundle exec linguist --breakdown
в своем каталоге приложения.
Кажется, что много дополнительной работы, особенно если у вас еще нет рельсов, но на самом деле вам не нужно ничего знать о рельсах, если вы выполните следующие действия, и я просто не нашел лучшего способа для обнаружения языка файла/фрагмента кода.
Ответ 15
Я считаю, что нет единого решения, которое могло бы определить, на каком языке находится фрагмент, только на основе этого единственного фрагмента. Возьмите ключевое слово print
. Он может появляться на любом количестве языков, каждый из которых предназначен для разных целей и имеет различный синтаксис.
У меня есть несколько советов. В настоящее время я пишу небольшой фрагмент кода для своего сайта, который можно использовать для идентификации языков программирования. Как и большинство других сообщений, может существовать диапазон огромных языков программирования, которые вы просто не слышали, вы не можете объяснить их всех.
Что я сделал, так это то, что каждый язык может быть идентифицирован с помощью выбора ключевых слов. Например, Python можно идентифицировать несколькими способами. Это, вероятно, проще, если вы выберете "черты", которые также, безусловно, уникальны для языка. Для Python я выбираю черту использования двоеточий, чтобы начать набор операторов, которые, я считаю, являются довольно уникальным признаком (исправьте меня, если я ошибаюсь).
Если в моем примере вы не можете найти двоеточие, чтобы запустить набор операторов, а затем перейдите на другой возможный признак, скажем, используя ключевое слово def
для определения функции. Теперь это может вызвать некоторые проблемы, поскольку Ruby также использует ключевое слово def
для определения функции. Ключом к тому, чтобы сказать обоим (Python и Ruby) в отдельности, является использование различных уровней фильтрации, чтобы получить наилучшее соответствие. Ruby использует ключевое слово end
, чтобы закончить функцию, тогда как Python не имеет ничего, чтобы закончить функцию, просто де-отступ, но вы не хотите туда идти. Но опять же, end
также может быть Lua, еще одним языком программирования для добавления в микс.
Вы можете видеть, что языки программирования просто слишком много перекрываются. Одним из ключевых слов, которые могут быть ключевым словом на одном языке, может быть ключевое слово на другом языке. Использование комбинации ключевых слов, которые часто идут вместе, например Java public static void main(String[] args)
, помогает устранить эти проблемы.
Как я уже сказал, ваш лучший шанс искать относительно уникальные ключевые слова или наборы ключевых слов для разделения одного от другого. И, если вы ошибаетесь, по крайней мере, вы поехали.
Ответ 16
Настройте случайный скремблер, например
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;