Jaki byłby najlepszy sposób na wykrycie, jaki język programowania jest używany we fragmencie kodu?
Jaki byłby najlepszy sposób na wykrycie, jaki język programowania jest używany we fragmencie kodu?
Odpowiedzi:
Myślę, że metoda zastosowana w filtrach antyspamowych działałaby bardzo dobrze. Podzieliłeś fragment na słowa. Następnie porównujesz występowanie tych słów ze znanymi fragmentami i obliczasz prawdopodobieństwo, że ten fragment jest napisany w języku X dla każdego języka, który Cię interesuje.
http://en.wikipedia.org/wiki/Bayesian_spam_filtering
Jeśli masz podstawowy mechanizm, bardzo łatwo jest dodać nowe języki: po prostu wytrenuj detektor z kilkoma fragmentami nowego języka (możesz przesłać mu projekt open source). W ten sposób uczy się, że „System” prawdopodobnie pojawi się we fragmentach kodu C #, a „puts” we fragmentach Rubiego.
W rzeczywistości użyłem tej metody, aby dodać wykrywanie języka do fragmentów kodu oprogramowania forum. Działało w 100%, z wyjątkiem niejednoznacznych przypadków:
print "Hello"
Pozwól mi znaleźć kod.
Nie mogłem znaleźć kodu, więc stworzyłem nowy. To trochę uproszczone, ale działa w moich testach. Obecnie, jeśli podasz mu znacznie więcej kodu Pythona niż kodu Ruby, prawdopodobnie powiesz, że ten kod:
def foo
puts "hi"
end
jest kodem Pythona (chociaż tak naprawdę jest to Ruby). Dzieje się tak, ponieważ Python również ma def
słowo kluczowe. Więc jeśli zobaczył 1000x def
w Pythonie i 100x def
w Rubim, może nadal mówić Python, mimo że puts
i end
jest specyficzny dla Rubiego. Możesz to naprawić, śledząc słowa widoczne w każdym języku i dzieląc je gdzieś (lub wprowadzając równe ilości kodu w każdym języku).
Mam nadzieję, że Ci to pomoże:
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)
$
, więc może nie powinieneś dzielić się na granice słów, ponieważ $
powinny one trzymać się zmiennej. Operatorzy lubią =>
i :=
powinni być trzymani razem jako pojedynczy token, ale OTH prawdopodobnie powinieneś podzielić się wokół {
s, ponieważ zawsze stoją samodzielnie.
Wykrywanie języka rozwiązane przez innych:
Podejście Ohloh: https://github.com/blackducksw/ohcount/
Podejście Github: https://github.com/github/linguist
Możesz znaleźć przydatne materiały tutaj: http://alexgorbatchev.com/wiki/SyntaxHighlighter . Alex spędził dużo czasu, zastanawiając się, jak przeanalizować wiele różnych języków i jakie są kluczowe elementy składni.
Guesslang to możliwe rozwiązanie:
http://guesslang.readthedocs.io/en/latest/index.html
Jest też SourceClassifier:
https://github.com/chrislo/sourceclassifier/tree/master
Zainteresowałem się tym problemem po znalezieniu kodu w artykule na blogu, którego nie mogłem zidentyfikować. Dodanie tej odpowiedzi, ponieważ to pytanie było pierwszym trafieniem w wyszukiwaniu hasła „zidentyfikuj język programowania”.
To bardzo trudne, a czasem niemożliwe. Z jakiego języka pochodzi ten krótki fragment?
int i = 5;
int k = 0;
for (int j = 100 ; j > i ; i++) {
j = j + 1000 / i;
k = k + i * j;
}
(Podpowiedź: może to być jeden z kilku.)
Możesz spróbować przeanalizować różne języki i spróbować zdecydować, korzystając z analizy częstotliwości słów kluczowych. Jeśli określone zestawy słów kluczowych występują z określoną częstotliwością w tekście, prawdopodobnie jest to język Java itp. Ale nie sądzę, abyś uzyskał coś, co jest całkowicie głupie, ponieważ możesz na przykład nazwać zmienną w C o tej samej nazwie jako słowo kluczowe w Javie, a analiza częstotliwości zostanie oszukana.
Jeśli podejmiesz wyższy poziom złożoności, możesz poszukać struktur, jeśli określone słowo kluczowe zawsze występuje po innym, dostaniesz więcej wskazówek. Ale będzie też znacznie trudniej zaprojektować i wdrożyć.
Alternatywą jest użycie highlight.js , która wykonuje podświetlanie składni, ale używa współczynnika powodzenia procesu podświetlania do identyfikacji języka. W zasadzie każda baza kodu podświetlająca składnię może być używana w ten sam sposób, ale fajną rzeczą w highlight.js jest to, że wykrywanie języka jest uważane za funkcję i jest używane do celów testowych .
AKTUALIZACJA: próbowałem tego i nie działało tak dobrze. Skompresowany JavaScript całkowicie go zdezorientował, tj. Tokenizer jest wrażliwy na białe znaki. Generalnie samo liczenie trafień w najciekawsze miejsca nie wydaje się zbyt wiarygodne. Silniejszy parser lub może niezrównana liczba sekcji może działać lepiej.
Najpierw spróbuję znaleźć konkretne słowa kluczowe języka, np
"package, class, implements "=> JAVA
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...
Zależałoby to od typu posiadanego fragmentu kodu, ale przepuściłbym go przez serię tokenizatorów i sprawdziłbym, w przypadku którego BNF języka jest ważny.
Niezła łamigłówka.
Myślę, że niemożliwe jest wykrycie wszystkich języków. Ale możesz uruchomić na kluczowych tokenach. (niektóre zastrzeżone słowa i często używane kombinacje znaków).
Ben, istnieje wiele języków o podobnej składni. Więc to zależy od rozmiaru fragmentu.
Prettify to pakiet Javascript, który dobrze wykrywa języki programowania:
http://code.google.com/p/google-code-prettify/
Jest to głównie narzędzie do podświetlania składni, ale prawdopodobnie istnieje sposób na wyodrębnienie części wykrywającej w celu wykrycia języka z fragmentu.
Potrzebowałem tego, więc stworzyłem własny. https://github.com/bertyhell/CodeClassifier
Można go bardzo łatwo rozszerzyć, dodając plik szkoleniowy w odpowiednim folderze. Napisane w C #. Ale wyobrażam sobie, że kod można łatwo przekonwertować na dowolny inny język.
Nie sądzę, że można to osiągnąć w łatwy sposób. Prawdopodobnie wygenerowałbym listy symboli / wspólnych słów kluczowych unikalnych dla niektórych języków / klas języków (np. Nawiasy klamrowe dla języka w stylu C, słowa kluczowe Dim i Sub dla języków BASIC, słowo kluczowe def dla Pythona, słowo kluczowe let dla języków funkcjonalnych) . Wtedy możesz użyć podstawowych funkcji składni, aby jeszcze bardziej zawęzić.
Myślę, że największą różnicą między językami jest ich struktura. Więc moim pomysłem byłoby przyjrzenie się pewnym wspólnym elementom we wszystkich językach i zobaczenie, jak się różnią. Na przykład możesz użyć wyrażeń regularnych, aby wybrać takie rzeczy, jak:
I może kilka innych rzeczy, które powinna mieć większość języków. Następnie użyj systemu punktowego. Przyznaj maksymalnie 1 punkt za każdy element, jeśli zostanie znalezione wyrażenie regularne. Oczywiście niektóre języki będą używać dokładnie tej samej składni (ponieważ pętle są często pisane w taki for(int i=0; i<x; ++i)
sposób, że wiele języków może dać punkt za tę samą rzecz, ale przynajmniej zmniejsza się prawdopodobieństwo, że jest to zupełnie inny język). Niektóre z nich mogą uzyskać 0 na całej tablicy (na przykład fragment w ogóle nie zawiera funkcji), ale to w porządku.
Połącz to z rozwiązaniem Julesa i powinno działać całkiem dobrze. Może też poszukaj częstotliwości słów kluczowych dla dodatkowego punktu.
Ciekawy. Mam podobne zadanie rozpoznawania tekstu w różnych formatach. Właściwości YAML, JSON, XML lub Java? Na przykład nawet w przypadku błędów składniowych powinienem z pewnością odróżnić JSON od XML.
Uważam, że sposób modelowania problemu jest krytyczny. Jak powiedział Mark, tokenizacja jednowyrazowa jest konieczna, ale prawdopodobnie nie wystarczy. Będziemy potrzebować bigramów, a nawet trygramów. Ale myślę, że możemy pójść dalej, wiedząc, że patrzymy na języki programowania. Zauważyłem, że prawie każdy język programowania ma dwa unikalne typy tokenów - symbole i słowa kluczowe . Symbole są stosunkowo łatwe do rozpoznania (niektóre symbole mogą być literałami, które nie są częścią języka). Wtedy bigramy lub trygramy symboli przejmą unikalne struktury składniowe wokół symboli. Słowa kluczowe to kolejny łatwy cel, jeśli zbiór treningowy jest wystarczająco duży i zróżnicowany. Przydatną funkcją mogą być duże ramki wokół możliwych słów kluczowych. Innym interesującym typem tokena są białe znaki. W rzeczywistości, jeśli tokenizujemy w zwykły sposób za pomocą białych znaków, utracimy te informacje. Powiedziałbym, że do analizowania języków programowania zachowujemy białe znaki, ponieważ mogą one zawierać przydatne informacje o strukturze składni.
Wreszcie, jeśli wybiorę klasyfikator, taki jak losowy las, przeszukam Github i zgromadzę cały publiczny kod źródłowy. Większość plików z kodem źródłowym można oznaczyć za pomocą sufiksu pliku. Dla każdego pliku losowo podzielę go w pustych wierszach na fragmenty o różnych rozmiarach. Następnie wyodrębnię cechy i nauczę klasyfikatora za pomocą oznaczonych fragmentów. Po zakończeniu treningu klasyfikator można przetestować pod kątem dokładności i przypominania.
Najlepszym rozwiązaniem, z jakim się spotkałem, jest użycie klejnotu lingwistycznego w aplikacji Ruby on Rails. To trochę specyficzny sposób, ale działa. Wspomniał o tym powyżej @nisc, ale powiem ci dokładnie, jak z niego korzystać. (Niektóre z poniższych poleceń wiersza poleceń są specyficzne dla systemu Ubuntu, ale powinny być łatwo przetłumaczone na inne systemy operacyjne)
Jeśli masz jakąkolwiek aplikację railsową, w której nie masz nic przeciwko tymczasowemu manipulowaniu, utwórz w niej nowy plik, aby wstawić odpowiedni fragment kodu. (Jeśli nie masz zainstalowanych railsów, jest tutaj dobry przewodnik , chociaż dla Ubuntu polecam to . Następnie uruchom rails new <name-your-app-dir>
i cd do tego katalogu. Wszystko, czego potrzebujesz do uruchomienia aplikacji railsowej, jest już dostępne).
Gdy masz już aplikację gem 'github-linguist'
railsową , z której możesz tego korzystać, dodaj do swojego Gemfile (dosłownie wywołane Gemfile
w katalogu aplikacji, bez rozszerzenia).
Następnie zainstaluj ruby-dev ( sudo apt-get install ruby-dev
)
Następnie zainstaluj cmake ( sudo apt-get install cmake
)
Teraz możesz uruchomić gem install github-linguist
(jeśli pojawi się błąd, który mówi, że wymagane jest icu, zrób sudo apt-get install libicu-dev
i spróbuj ponownie)
(Może być konieczne wykonanie sudo apt-get update
lub sudo apt-get install make
lub sudo apt-get install build-essential
jeśli powyższe nie zadziałało)
Teraz wszystko jest gotowe. Możesz teraz użyć tego w dowolnym momencie, gdy chcesz sprawdzić fragmenty kodu. W edytorze tekstu otwórz plik, który utworzyłeś, aby wstawić fragment kodu (powiedzmy, że jest to, app/test.tpl
ale jeśli znasz rozszerzenie fragmentu, użyj go zamiast .tpl
. Jeśli nie znasz rozszerzenia, nie używaj go ). Teraz wklej swój fragment kodu w tym pliku. Przejdź do wiersza poleceń i uruchom bundle install
(musi znajdować się w katalogu aplikacji). Następnie uruchom linguist app/test.tpl
(bardziej ogólnie linguist <path-to-code-snippet-file>
). Podaje typ, typ MIME i język. W przypadku wielu plików (lub do ogólnego użytku z aplikacją ruby / rails) możesz uruchomić bundle exec linguist --breakdown
w katalogu swojej aplikacji.
Wydaje się, że to dużo dodatkowej pracy, zwłaszcza jeśli nie masz jeszcze szyn, ale tak naprawdę nie musisz niczego wiedzieć o szynach, jeśli wykonasz te kroki, a ja naprawdę nie znalazłem lepszego sposobu na wykrycie język pliku / fragmentu kodu.
Uważam, że nie ma jednego rozwiązania, które mogłoby zidentyfikować język, w którym znajduje się fragment, tylko na podstawie tego pojedynczego fragmentu. Weź słowo kluczowe print
. Może pojawić się w dowolnej liczbie języków, z których każdy służy do innych celów i mieć inną składnię.
Mam kilka rad. Obecnie piszę mały fragment kodu dla mojej witryny internetowej, którego można użyć do identyfikacji języków programowania. Podobnie jak większość innych postów, może istnieć ogromna liczba języków programowania, których po prostu nie słyszałeś, nie możesz ich wszystkich wyjaśnić.
Zrobiłem to, że każdy język można zidentyfikować za pomocą wybranych słów kluczowych. Na przykład Python można zidentyfikować na wiele sposobów. Prawdopodobnie jest to łatwiejsze, jeśli wybierzesz „cechy”, które są również z pewnością unikalne dla języka. W przypadku Pythona wybieram cechę używania dwukropków do rozpoczynania zestawu instrukcji, co moim zdaniem jest dość wyjątkową cechą (popraw mnie, jeśli się mylę).
Jeśli w moim przykładzie nie możesz znaleźć dwukropka, aby rozpocząć zestaw instrukcji, przejdź do innej możliwej cechy, powiedzmy, używając def
słowa kluczowego do zdefiniowania funkcji. Może to powodować pewne problemy, ponieważ Ruby używa słowa kluczowego również def
do definiowania funkcji. Kluczem do odróżnienia tych dwóch (Python i Ruby) jest użycie różnych poziomów filtrowania, aby uzyskać najlepsze dopasowanie. Ruby używa słowa kluczowego, end
aby zakończyć funkcję, podczas gdy Python nie ma nic do zakończenia funkcji, po prostu usuwa wcięcie, ale nie chcesz tam iść. Ale znowu end
może to być Lua, kolejny język programowania, który można dodać do mieszanki.
Widać, że języki programowania po prostu nakładają się zbyt mocno. Jedno słowo kluczowe, które może być słowem kluczowym w jednym języku, może być słowem kluczowym w innym języku. Używanie kombinacji słów kluczowych, które często idą w parze, na przykład w języku Java, public static void main(String[] args)
pomaga wyeliminować te problemy.
Jak już powiedziałem, największą szansą jest szukanie stosunkowo unikalnych słów kluczowych lub zestawów słów kluczowych, aby oddzielić je od siebie. A jeśli się pomylisz, przynajmniej spróbowałeś.
Ta witryna wydaje się całkiem dobra w identyfikowaniu języków, jeśli chcesz szybko wkleić fragment kodu do formularza internetowego, zamiast robić to programowo: http://dpaste.com/