Bir kod parçacığında hangi programlama dilinin kullanıldığını tespit etmenin en iyi yolu nedir?
Bir kod parçacığında hangi programlama dilinin kullanıldığını tespit etmenin en iyi yolu nedir?
Yanıtlar:
Spam filtrelerinde kullanılan yöntemin çok iyi çalışacağını düşünüyorum. Snippet'i kelimelere ayırırsınız. Ardından, bu kelimelerin geçtiği yerleri bilinen parçacıklarla karşılaştırırsınız ve bu ön bilginin ilgilendiğiniz her dil için X dilinde yazılma olasılığını hesaplarsınız.
http://en.wikipedia.org/wiki/Bayesian_spam_filtering
Temel mekanizmaya sahipseniz, yeni diller eklemek çok kolaydır: dedektörü yeni dilde birkaç parçacıkla eğitin (onu açık kaynaklı bir proje olarak besleyebilirsiniz). Bu şekilde "Sistem" in muhtemelen C # parçacıkları içinde görüneceğini ve Ruby parçacıklarında "koyduğunu" öğrenir.
Aslında bu yöntemi, forum yazılımı için kod parçacıklarına dil algılaması eklemek için kullandım. Belirsiz durumlar dışında, zamanın% 100'ünde çalıştı:
print "Hello"
Kodu bulmama izin ver.
Kodu bulamadım, bu yüzden yeni bir tane yaptım. Biraz basit ama testlerim için işe yarıyor. Şu anda, Ruby kodundan çok daha fazla Python kodu beslerseniz, muhtemelen bu kodun:
def foo
puts "hi"
end
Python kodudur (gerçekten Ruby olmasına rağmen). Bunun nedeni Python'un da bir def
anahtar kelimeye sahip olmasıdır . Dolayısıyla def
, Python'da 1000x ve def
Ruby'de 100x gördüyse , yine de Python diyebilir puts
veend
Yakut özgüdür. Bunu, dil başına görülen sözcükleri takip ederek ve bir yerde buna bölerek (veya her dilde eşit miktarda kod besleyerek) düzeltebilirsiniz.
Umarım size yardımcı olur:
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)
$
bu yüzden belki, olmamalıdır çünkü, kelime sınırları üzerinde bölme olmak $
değişkenle kalmalıdırlar. Gibi Operatörler =>
ve :=
tek göstergesi olarak sıkışmış bir arada olmalı, ancak OTH muhtemelen gerektiğini etrafında bölünmüş {
her zaman kendi başlarına ayakta çünkü s.
Başkaları tarafından çözülen dil tespiti:
Ohloh'un yaklaşımı: https://github.com/blackducksw/ohcount/
Github'ın yaklaşımı: https://github.com/github/linguist
Burada bazı yararlı materyaller bulabilirsiniz: http://alexgorbatchev.com/wiki/SyntaxHighlighter . Alex, çok sayıda farklı dilin nasıl ayrıştırılacağını ve anahtar sözdizimi öğelerinin ne olduğunu bulmak için çok zaman harcadı.
Guesslang olası bir çözümdür:
http://guesslang.readthedocs.io/en/latest/index.html
SourceClassifier da var:
https://github.com/chrislo/sourceclassifier/tree/master
Bir blog yazısında tanımlayamadığım bir kod bulduktan sonra bu problemle ilgilenmeye başladım. Bu soru "programlama dilini tanımla" için ilk arama sonucu olduğundan bu yanıtı eklemek.
Bu çok zor ve bazen imkansız. Bu kısa pasaj hangi dilden?
int i = 5;
int k = 0;
for (int j = 100 ; j > i ; i++) {
j = j + 1000 / i;
k = k + i * j;
}
(İpucu: Birkaç taneden herhangi biri olabilir.)
Çeşitli dilleri analiz etmeyi deneyebilir ve anahtar kelimelerin frekans analizini kullanmaya karar verebilirsiniz. Bir metinde belirli sıklıklarda belirli anahtar kelime grupları ortaya çıkarsa, büyük olasılıkla dil Java, vb. Java'da bir anahtar kelime olarak ve frekans analizi yanıltıcı olacaktır.
Karmaşıklıkta bir çentik alırsanız, yapıları arayabilirsiniz, eğer belirli bir anahtar kelime her zaman diğerinden sonra gelirse, bu size daha fazla ipucu verecektir. Ancak tasarımı ve uygulaması da çok daha zor olacaktır.
Alternatif bir kullanım için highlight.js gerçekleştirdiği dizim ama dilini tanımlamak için vurgulama sürecin başarısı hızı kullanmasıdır. Prensipte, herhangi bir sözdizimi vurgulayıcı kod tabanı aynı şekilde kullanılabilir, ancak vurgu.js ile ilgili güzel olan şey, dil algılamanın bir özellik olarak kabul edilmesi ve test amacıyla kullanılmasıdır .
GÜNCELLEME: Bunu denedim ve o kadar da işe yaramadı. Sıkıştırılmış JavaScript tamamen karıştırdı, yani belirteç boşluklara duyarlıdır. Genel olarak, sadece vurgu vuruşlarını saymak çok güvenilir görünmüyor. Daha güçlü bir ayrıştırıcı veya belki de eşleşmeyen bölüm sayıları daha iyi çalışabilir.
İlk olarak, bir dilin belirli anahtar işlerini bulmaya çalışırdım.
"package, class, implements "=> JAVA
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...
Ne tür bir parçacığa sahip olduğunuza bağlıydı, ancak onu bir dizi belirteçten geçirip hangi dilin BNF'sine karşı geçerli olduğunu görüyorum.
Güzel bulmaca.
Tüm dilleri tespit etmenin imkansız olduğunu düşünüyorum. Ancak anahtar jetonları tetikleyebilirsiniz. (belirli ayrılmış sözcükler ve sıklıkla kullanılan karakter kombinasyonları).
Ben, benzer sözdizimine sahip birçok dil var. Dolayısıyla, pasajın boyutuna bağlıdır.
Prettify, programlama dillerini tespit etmede başarılı olan bir Javascript paketidir:
http://code.google.com/p/google-code-prettify/
Esas olarak bir sözdizimi vurgulayıcıdır, ancak muhtemelen bir pasajdan dili tespit etmek amacıyla algılama bölümünü çıkarmanın bir yolu vardır.
Buna ihtiyacım vardı, bu yüzden kendiminkini yarattım. https://github.com/bertyhell/CodeClassifier
Doğru klasöre bir eğitim dosyası ekleyerek çok kolay bir şekilde genişletilebilir. C # ile yazılmıştır. Ancak kodun başka bir dile kolayca dönüştürülebileceğini düşünüyorum.
Bunu başarmanın kolay bir yolu olacağını düşünmüyorum. Muhtemelen belirli dillere / dil sınıflarına özgü sembol / ortak anahtar sözcük listeleri oluşturabilirim (örneğin, C-stili dil için küme parantezleri, BASIC dilleri için Dim ve Sub anahtar sözcükleri, Python için def anahtar sözcüğü, işlevsel diller için let anahtar sözcüğü) . Daha sonra, onu daha da daraltmak için temel sözdizimi özelliklerini kullanabilirsiniz.
Bence diller arasındaki en büyük ayrım yapısıdır. Yani benim fikrim, tüm dillerdeki belirli ortak öğelere bakmak ve nasıl farklı olduklarını görmek olacaktır. Örneğin, aşağıdaki gibi şeyleri seçmek için normal ifadeleri kullanabilirsiniz:
Ve belki çoğu dilin sahip olması gereken birkaç şey daha. O zaman bir puan sistemi kullanın. Normal ifade bulunursa, her öğe için en fazla 1 puan verin. Açıkçası, bazı diller tam olarak aynı sözdizimini kullanacaktır (çünkü döngüler genellikle şöyle yazılırfor(int i=0; i<x; ++i)
birden çok dil , her biri aynı şey için bir puan alabilir, ancak en azından tamamen farklı bir dil olma olasılığını azaltıyorsunuz). Bazıları tahtada 0 puan alabilir (örneğin, pasajda bir işlev yoktur), ancak bu tamamen iyidir.
Bunu Jules'un çözümüyle birleştirin ve oldukça iyi çalışacaktır. Ekstra bir nokta için anahtar kelimelerin sıklıklarına da bakabilirsiniz.
İlginç. Farklı formatlardaki metni tanımak için benzer bir görevim var. YAML, JSON, XML veya Java özellikleri? Sözdizimi hatalarında bile, örneğin JSON'u XML'den güvenle ayırmalıyım.
Sorunu nasıl modellediğimizin kritik olduğunu anladım. Mark'ın dediği gibi, tek kelimelik tokenleştirme gereklidir, ancak muhtemelen yeterli değildir. Bigramlara ve hatta trigramlara ihtiyacımız olacak. Ama bence programlama dillerine baktığımızı bilerek oradan daha da ileri gidebiliriz. Hemen hemen her programlama dilinin iki benzersiz simge türü olduğunu fark ettim - semboller ve anahtar kelimeler . Sembollerin tanınması nispeten kolaydır (bazı semboller, dilin bir parçası olmayan gerçek bilgiler olabilir). Ardından, sembollerin bigramları veya trigramları, sembollerin etrafındaki benzersiz sözdizimi yapılarını alır. Eğitim seti yeterince büyük ve çeşitli ise, anahtar kelimeler başka bir kolay hedeftir. Yararlı bir özellik, olası anahtar kelimelerin etrafındaki büyük yazılar olabilir. Bir başka ilginç simge türü boşluktur. Aslında beyaz boşlukla her zamanki gibi belirteceksek, bu bilgiyi kaybedeceğiz. Diyorum ki, programlama dillerini analiz etmek için, sözdizimi yapısı hakkında yararlı bilgiler taşıyabileceğinden boşluk belirteçlerini tutuyoruz.
Son olarak, rastgele orman gibi bir sınıflandırıcı seçersem, github'ı tarar ve tüm genel kaynak kodunu toplar. Kaynak kod dosyasının çoğu dosya son eki ile etiketlenebilir. Her dosya için, boş satırlarda rastgele farklı boyutlarda parçacıklara ayıracağım. Daha sonra özellikleri çıkaracağım ve sınıflandırıcıyı etiketli parçacıkları kullanarak eğiteceğim. Eğitim tamamlandıktan sonra sınıflandırıcı, hassasiyet ve geri çağırma açısından test edilebilir.
Karşılaştığım en iyi çözüm, Ruby on Rails uygulamasında dilbilimci cevherini kullanmaktır . Bunu yapmanın belirli bir yolu ama işe yarıyor. Bu yukarıda @nisc tarafından belirtilmişti, ancak size onu kullanmak için tam adımlarımı anlatacağım. (Aşağıdaki komut satırı komutlarından bazıları ubuntu'ya özeldir ancak diğer işletim sistemlerine kolayca çevrilmelidir)
Geçici olarak uğraşmaktan çekinmediğiniz herhangi bir ray uygulamanız varsa, söz konusu kod parçacığını eklemek için yeni bir dosya oluşturun. (Eğer yoksa raylar iyi bir rehber var yüklü burada ubuntu için tavsiye rağmen bu . O zaman kaç rails new <name-your-app-dir>
ve bu dizine cd. Her şey çalıştırmak için gereken bir uygulama var zaten raylar).
Bunu kullanabileceğiniz bir ray uygulaması oluşturduktan gem 'github-linguist'
sonra, Gemfile'ınıza ekleyin (kelimenin tam anlamıyla Gemfile
uygulama dizininizde çağrılır , dahili yok).
Ruby-dev ( sudo apt-get install ruby-dev
) 'i kurun
Ardından cmake ( sudo apt-get install cmake
)
Artık çalıştırabilirsiniz gem install github-linguist
(icu'nun gerekli olduğunu belirten bir hata alırsanız yapın sudo apt-get install libicu-dev
ve tekrar deneyin)
(Bir yapmanız gerekebilir sudo apt-get update
veya sudo apt-get install make
veya sudo apt-get install build-essential
yukarıdaki iş olmasaydı)
Şimdi her şey ayarlandı. Artık bunu, kod parçacıklarını kontrol etmek istediğiniz her zaman kullanabilirsiniz. Bir metin düzenleyicide, kod pasajınızı eklemek için oluşturduğunuz dosyayı açın (diyelim, app/test.tpl
ancak pasajınızın uzantısını biliyorsanız yerine onu kullanın .tpl
. Uzantıyı bilmiyorsanız, bir tane kullanmayın. ). Şimdi kod pasajınızı bu dosyaya yapıştırın. Komut satırına gidin ve çalıştırın bundle install
(uygulamanızın dizininde olmalıdır). Ardından çalıştırın linguist app/test.tpl
(daha genel olarak linguist <path-to-code-snippet-file>
). Size türü, mime türü ve dili söyleyecektir. Birden çok dosya için (veya bir ruby / rails uygulamasıyla genel kullanım için) bundle exec linguist --breakdown
uygulamanızın dizininde çalıştırabilirsiniz .
Bu çok fazla ekstra iş gibi görünüyor, özellikle halihazırda raylarınız yoksa, ancak bu adımları izlerseniz raylar hakkında HİÇBİR ŞEY bilmenize gerek yok ve ben sadece bunu tespit etmenin daha iyi bir yolunu bulamadım. bir dosya / kod parçacığının dili.
Bir parçacığın hangi dilde olduğunu belirleyebilecek tek bir çözüm olmadığına inanıyorum, sadece o tek parçacığa dayanarak. Anahtar kelimeyi alın print
. Her biri farklı amaçlara yönelik olan ve farklı sözdizimine sahip herhangi bir sayıda dilde görünebilir.
Bazı tavsiyem var. Şu anda web sitem için programlama dillerini tanımlamak için kullanılabilecek küçük bir kod yazıyorum. Diğer gönderilerin çoğu gibi, duymadığınız çok çeşitli programlama dilleri olabilir, hepsini açıklayamazsınız.
Yaptığım şey, her dilin bir dizi anahtar kelime ile tanımlanabilmesiydi. Örneğin, Python birkaç şekilde tanımlanabilir. Dil için de kesinlikle benzersiz olan 'özellikleri' seçerseniz muhtemelen daha kolay. Python için, oldukça benzersiz bir özellik olduğuna inandığım bir dizi ifadeye başlamak için iki nokta üst üste kullanma özelliğini seçiyorum (yanılıyorsam düzeltin).
Örneğimde, bir ifade kümesini başlatmak için iki nokta üst üste bulamazsanız, daha sonra olası başka bir özelliğe geçerseniz, diyelim ki def
bir işlevi tanımlamak için anahtar sözcüğü kullanın. Bu, bazı sorunlara neden olabilir, çünkü Ruby ayrıca def
bir işlevi tanımlamak için anahtar sözcüğü kullanır . İkisini (Python ve Ruby) birbirinden ayırmanın anahtarı, en iyi eşleşmeyi elde etmek için çeşitli filtreleme seviyelerini kullanmaktır. Ruby end
bir işlevi bitirmek için anahtar kelimeyi kullanır, oysa Python'un bir işlevi bitirmek için hiçbir şeyi yoktur, sadece girintiyi kaldırır, ancak oraya gitmek istemezsiniz. Ancak yine end
de, karışıma eklenecek başka bir programlama dili olan Lua da olabilir.
Programlama dillerinin çok fazla örtüştüğünü görebilirsiniz. Bir dilde anahtar kelime olabilecek bir anahtar kelime, başka bir dilde anahtar kelime olabilir. Java'nınki gibi, genellikle bir araya gelen bir anahtar kelime kombinasyonu kullanmak, public static void main(String[] args)
bu sorunları ortadan kaldırmaya yardımcı olur.
Daha önce de söylediğim gibi, en iyi şansınız, birini diğerinden ayırmak için nispeten benzersiz anahtar kelimeler veya anahtar kelime grupları aramaktır. Ve eğer yanlış anlarsan, en azından bir şansın vardı.
Programlı olarak yapmak yerine bir web formuna pasajı yapıştırmanın hızlı bir yolunu istiyorsanız, bu site dilleri tanımlamada oldukça başarılı görünüyor: http://dpaste.com/