Виявлення мови програмування з фрагмента


115

Що було б найкращим способом виявити, яка мова програмування використовується у фрагменті коду?


1
Існує практично нескінченна кількість мов ... Ви хочете виявити будь-яку з них? Або ми просто говоримо про популярні?
Спенсер Рупорт

Просто популярні (C / C ++, C #, Java, Pascal, Python, VB.NET. PHP, JavaScript і, можливо, Haskell).
Жоао Матос

12
Ну Haskell не може бути популярним, оскільки я ніколи про нього не чув. ;-)
Стефанія Сторінка

22
Напевно ви мало знаєте про мови програмування, якщо ви не чули про Haskell.
Ахорус

4
Є цей онлайн-сервіс, який робить це: algormia.com/algorithms/PetiteProgrammer/…
Benny Neugebauer

Відповіді:


99

Я думаю, що метод, який використовується в спам-фільтрах, буде працювати дуже добре. Ви розділите фрагмент на слова. Потім ви порівнюєте виникнення цих слів із відомими фрагментами та обчислюєте ймовірність того, що цей фрагмент написаний мовою X для кожної мови, яка вас цікавить.

http://en.wikipedia.org/wiki/Bayesian_spam_filtering

Якщо у вас є основний механізм, то додавати нові мови дуже просто: просто навчіть детектор декількома фрагментами нової мови (ви могли б подати це проект з відкритим кодом). Таким чином він дізнається, що "Система", ймовірно, з'явиться в C # фрагментах і "ставить" в фрагменти 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)

1
Мені також потрібно використовувати його у програмному забезпеченні форуму. Дякуємо за пораду про байєсівську фільтрацію.
Жоао Матос

12
Я щось подібне зробив у своєму класі NLP, але ми зробили це на крок далі. Вам не подобається дивитися на частоти одного слова, а пари і трійки слів. Наприклад, "public" може бути ключовим словом у багатьох мовах, але "public static void" більш поширений для C #. Якщо потрійного неможливо знайти, ви повернетесь до рівня 2, а потім 1.
червня

1
Ви також можете подумати про те, де ви розділяєте слова. У PHP змінні починаються з $, тому, можливо, вам не слід розбиватися на межі слів, тому що вони $повинні дотримуватися змінної. Оператори люблять =>і :=повинні бути скріплені як один маркер, але OTH ви, мабуть, повинні розділитися навколо {s, оскільки вони завжди стоять самостійно.
mpen

2
Так. Спосіб уникнути розщеплення взагалі - це використовувати ngram: ви берете кожну прядку по довжині. Наприклад, 5-грамовим знаком "put foo" є "put" "uts f", "ts fo" та "s foo". Ця стратегія може здатися дивною, але вона працює краще, ніж ви могли б подумати, це просто не те, як людина вирішить проблему. Щоб вирішити, який метод працює краще, вам доведеться перевірити обидва ...
Жуль

2
Деякі мови мають дуже мало синтаксису. Я також припускаю, що загальні назви змінних будуть домінувати над ключовими словами мови. В основному, якщо у ваших даних про навчання є шматочок коду С, написаний угорцем, зі змінними іменами та коментарями угорською мовою, то будь-яке інше джерело з угорською мовою в ньому, швидше за все, буде визначено "схожим".
tripleee

26

Виявлення мови, вирішене іншими:

Підхід Олоха: https://github.com/blackducksw/ohcount/

Підхід Гітхуба: https://github.com/github/linguist


4
Я вивчив обидва ці рішення, і жоден з них не зроблять саме те, що було запропоновано. Вони в основному розглядають розширення файлів, щоб визначити мову, тому не можуть обов'язково вивчити фрагмент без підказки з розширення.
Хокі

5
Підхід Гітхуба тепер включає також баєсовський класифікатор. Він насамперед виявляє кандидата на мові на основі розширення файлу, але коли розширення файлу відповідає декільком кандидатам (наприклад, ".h" -> C, C ++, ObjC), воно буде маркірувати зразок вхідного коду та класифікувати за попередньо підготовленим набором даних. Версію Github можна змусити сканувати код завжди, не дивлячись також на розширення.
Бензі

7

Тут ви можете знайти корисний матеріал: http://alexgorbatchev.com/wiki/SyntaxHighlighter . Алекс витратив багато часу на роздуми про те, як розібрати велику кількість різних мов та які ключові елементи синтаксису.


3
Посилання мертва. Здається, сюди переїхали: alexgorbatchev.com/SyntaxHighlighter
Moonchild

7

Guesslang - це можливе рішення:

http://guesslang.readthedocs.io/en/latest/index.html

Є також SourceClassifier:

https://github.com/chrislo/sourceclassifier/tree/master

Я зацікавився цією проблемою, знайшовши якийсь код у статті блогу, який я не зміг визначити. Додавання цієї відповіді, оскільки це питання було першим пошуковим хітом для "визначення мови програмування".


5

Це дуже важко, а іноді і неможливо. З якої мови цей короткий фрагмент?

int i = 5;
int k = 0;
for (int j = 100 ; j > i ; i++) {
    j = j + 1000 / i;
    k = k + i * j;
}

(Підказка: Це може бути будь-який із кількох.)

Ви можете спробувати проаналізувати різні мови та спробувати вирішити, використовуючи частотний аналіз ключових слів. Якщо певні набори ключових слів трапляються з певною частотою в тексті, цілком ймовірно, що мова - це Java і т. Д. Але я не думаю, що ви отримаєте щось, що є абсолютно неправдивим доказом, як ви могли б назвати, наприклад, змінну в однойменному C як ключове слово на Java, і аналіз частоти буде обдурений.

Якщо ви візьмете його за високу складність, ви можете шукати структури, якщо певне ключове слово завжди з’являється за іншим, то ви отримаєте більше підказок. Але також буде набагато складніше розробити та реалізувати.


26
Ну, якщо є кілька мов, детектор може просто дати всі можливі кандидати.
Стівен Хар'янто

Або він може дати перше, що відповідає. Якщо випадок використання в реальному світі є чимось на зразок підсвічування синтаксису, то це насправді не мало би значення. Це означає, що будь-яка з відповідних мов призведе до правильного виділення коду.
jonschlinkert

5

Альтернативою є використання naglasio.js , який виконує підсвічування синтаксису, але використовує коефіцієнт успішності процесу виділення для ідентифікації мови. В принципі, будь-яку базу коду для виділення синтаксису можна використовувати так само, але приємно про підкреслити.js - це те, що виявлення мови вважається функцією і використовується для тестування .

ОНОВЛЕННЯ: Я спробував це, і це не так добре вийшло. Стислий JavaScript повністю заплутав його, тобто токенізатор чутливий до простору. Взагалі, лише підрахунок видатних моментів здається не дуже надійним. Сильніший аналізатор або, можливо, незрівнянний підрахунок розділів, може працювати краще.


Мовні дані, що містяться у select.js, обмежуються значеннями, необхідними для виділення, що виявляється цілком недостатнім для виявлення мови (особливо для невеликої кількості коду).
Адам Кеннеді

Я думаю, що це добре, перевірте цю скрипку jsfiddle.net/3tgjnz10
sebilasse

4

По-перше, я б спробував знайти конкретні ключові роботи мови, наприклад

"package, class, implements "=> JAVA
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...

3
Проблема полягає в тому, що ці ключові слова все ще можуть з’являтися будь-якою мовою, як назви змінних, так і в рядках. Це, і в ключових словах багато перетинається. Вам доведеться зробити більше, ніж просто переглянути ключові слова.
mpen

2

Це залежало б від того, який тип фрагменту у вас є, але я провів би його через серію токенізаторів і побачив, на якій мові BNF він вважається дійсним.


Усі мови навіть не можуть бути описані BNF. Якщо вам дозволяється переглядати ключові слова та створювати макроси, це стає набагато складніше. Так само, як ми говоримо про фрагмент, вам доведеться провести частковий збіг проти BNF, що складніше і більш схильне до помилок.

2

Гарна головоломка.

Я думаю, що виявити всі мови неможливо. Але ви можете запустити ключові маркери. (певні зарезервовані слова та часто використовувані комбінації символів).

Бен, існує багато мов з подібним синтаксисом. Тож це залежить від розміру фрагмента.


1

Prettify - це пакет Javascript, який виконує нормальну роботу з виявлення мов програмування:

http://code.google.com/p/google-code-prettify/

Це головним чином виділення синтаксису, але, мабуть, існує спосіб витягнути частину виявлення для виявлення мови з фрагмента.


1
При подальшому огляді здається, що вишуканий насправді не виявляє мову, але він виділяється відповідно до синтаксису кожного елемента.
Хокі


1

Мені це було потрібно, тому я створив свою власну. https://github.com/bertyhell/CodeClassifier

Це дуже легко розширюється, додаючи навчальний файл у правильну папку. Написано на c #. Але я думаю, що код легко перетворюється на будь-яку іншу мову.


0

Я не думаю, що це буде легким способом досягти цього. Я, мабуть, генерує списки символів / загальних ключових слів, унікальних для певних мов / класів мов (наприклад, фігурні дужки для мови у стилі C, ключові слова Dim та Sub для мов BASIC, ключове слово def для Python, ключове слово let для функціональних мов) . Тоді ви зможете використовувати основні функції синтаксису, щоб ще більше звузити його.


0

Я думаю, що найбільша відмінність між мовами - це її структура. Тож моя ідея полягала б у тому, щоб переглянути певні загальні елементи на всіх мовах і побачити, як вони відрізняються. Наприклад, ви можете використовувати регулярні вирази, щоб вибрати такі речі, як:

  • визначення функцій
  • мінливі декларації
  • декларації класу
  • коментарі
  • для петель
  • при цьому петлі
  • друкувати заяви

І, можливо, ще кілька речей, якими повинна володіти більшість мов. Потім використовуйте точкову систему. Надайте максимум 1 бал за кожен елемент, якщо буде знайдено регулярний вираз. Очевидно, деякі мови використовуватимуть такий самий синтаксис (для циклів часто пишуться якfor(int i=0; i<x; ++i) так, декількох мовах кожен може набрати бал за одне і те ж, але принаймні ви зменшуєте ймовірність того, що це буде зовсім інша мова). Деякі з них можуть набирати 0s по всій дошці (наприклад, фрагмент взагалі не містить функції), але це прекрасно.

Поєднайте це з рішенням Жуля, і воно повинно працювати досить добре. Можливо, також шукайте частоту ключових слів для додаткової точки.


0

Цікаво. У мене є аналогічне завдання розпізнавати текст у різних форматах. Властивості YAML, JSON, XML або Java? Навіть із синтаксичними помилками, наприклад, я мушу впевнено відокремлювати JSON від XML.

Я вважаю, як ми моделюємо проблему критично. Як зазначив Марк, однословна токенізація необхідна, але, ймовірно, недостатня. Нам знадобляться біграми, а то й триграми. Але я думаю, що ми можемо піти далі звідти, знаючи, що ми дивимося на мови програмування. Я зауважую, що майже будь-яка мова програмування має два унікальних типу лексем - символи та ключові слова . Символи розпізнати порівняно просто (деякі символи можуть бути буквами, які не є частиною мови). Тоді біграми або триграми символів підберуть унікальні синтаксичні структури навколо символів. Ключові слова - ще одна проста ціль, якщо навчальний набір достатньо великий та різноманітний. Корисною функцією можуть бути біграми навколо можливих ключових слів. Ще один цікавий тип жетонів - пробіл. Насправді, якщо ми будемо токенізувати звичайним способом білим простором, ми втратимо цю інформацію. Я б сказав, що для аналізу мов програмування ми зберігаємо маркери пробілів, оскільки це може містити корисну інформацію про структуру синтаксису.

Нарешті, якщо я виберу такий класифікатор, як випадковий ліс, я проскакую github і зберу весь відкритий код. Більшість файлів вихідного коду можна мітити суфіксом файлу. Для кожного файлу я його випадковим чином розділяю на порожні рядки на фрагменти різного розміру. Потім я витягую функції та треную класифікатор за допомогою позначених фрагментів. Після закінчення навчання класифікатор можна перевірити на точність та відкликати.


0

Найкраще рішення, з яким я зіткнувся, - це використання мовного коштовного каменя в додатку Ruby on Rails. Це певний спосіб зробити це, але він працює. Про це згадував @nisc, але я розповім точні кроки щодо його використання. (Деякі з наведених нижче командних команд є типовими для ubuntu, але їх слід легко перекласти в інші ОС)

Якщо у вас є програма з рейками, з якою ви не заперечуєте тимчасово возитися, створіть у ній новий файл, щоб вставити відповідний фрагмент коду. (Якщо у вас немає рейки встановлені там хороший гід тут , хоча для Убунту я рекомендую це . Потім запустіть rails new <name-your-app-dir>і перейдіть в цей каталог. Всі , що вам потрібно запустити додаток рейки вже є).

Після того, як у вас є додаток для рейкових рейсів, ви можете це використовувати, додайте gem 'github-linguist'до свого Gemfile (буквально щойно викликається Gemfileв каталозі додатків, без доп.)

Потім встановіть 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 та мову. Для декількох файлів (або для загального використання з додатком rubin / rails) можна запуститись bundle exec linguist --breakdownу довіднику програми.

Це здається великою кількістю зайвої роботи, особливо якщо у вас вже немає рейок, але вам насправді не потрібно знати нічого про рейки, якщо ви дотримуєтесь цих кроків, і я просто не знайшов кращого способу виявити мова фрагмента файлу / коду.


0

Я вважаю, що не існує єдиного рішення, яке могло б визначити, на якій мові є фрагмент, лише на основі цього окремого фрагмента. Візьміть ключове слово print. Він може з’являтися на будь-якій кількості мов, кожна з яких має різні цілі, і мати різний синтаксис.

У мене є поради. Зараз я пишу невеликий фрагмент коду для свого веб-сайту, який можна використовувати для ідентифікації мов програмування. Як і більшість інших дописів, може існувати величезний спектр мов програмування, які ви просто не чули, ви не можете їх пояснити.

Що я зробив, це те, що кожну мову можна визначити за допомогою вибору ключових слів. Наприклад, Python можна було ідентифікувати кількома способами. Напевно, простіше, якщо ви виберете «риси», які, безумовно, унікальні для мови. Для Python я вибираю рису використання колонок для початку набору висловлювань, які, на мою думку, є досить унікальною рисою (виправте мене, якщо я помиляюся).

Якщо у моєму прикладі ви не можете знайти двокрапки для початку набору операторів, тоді перейдіть до іншої можливої ​​ознаки, скажімо, використовуючи defключове слово для визначення функції. Тепер це може спричинити деякі проблеми, тому що Ruby також використовує ключове слово defдля визначення функції. Ключовим моментом, щоб розказати двох (Python та Ruby), є використання різних рівнів фільтрації, щоб отримати найкращу відповідність. Ruby використовує ключове слово endдля завершення функції, тоді як Python не має нічого, щоб закінчити функцію, лише відступ, але ви не хочете туди йти. Але знову ж таки, endможе бути Lua, ще одна мова програмування, яку слід додати до суміші.

Видно, що мови програмування просто занадто сильно накладаються. Одне ключове слово, яке може бути ключовим словом на одній мові, може бути ключовим словом на іншій мові. Використання комбінації ключових слів, які часто поєднуються, як-от Java, public static void main(String[] args)допомагає усунути ці проблеми.

Як я вже говорив, ваш найкращий шанс шукати відносно унікальні ключові слова або набори ключових слів, щоб відокремити одне від іншого. І, якщо ви помиляєтесь, принаймні, у вас був похід.


0

Встановіть випадковий скремблер, як

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

Цей сайт здається досить гарним у визначенні мов, якщо ви хочете швидкий спосіб вставити фрагмент у веб-форму, а не робити це програмно: http://dpaste.com/

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.