Bir kod parçacığından programlama dilini algılama


116

Bir kod parçacığında hangi programlama dilinin kullanıldığını tespit etmenin en iyi yolu nedir?


1
Orada pratik olarak sonsuz sayıda dil var ... HERHANGİ BİRİNİ tespit etmek istiyor musunuz? Yoksa sadece popüler olanlardan mı bahsediyoruz?
Spencer Ruport

Sadece popüler olanlar (C / C ++, C #, Java, Pascal, Python, VB.NET. PHP, JavaScript ve belki Haskell).
João Matos

12
Haskell hiç duymadığım için popüler olamaz. ;-)
Stephanie Sayfa

22
Haskell'i duymadıysanız, muhtemelen programlama dilleri hakkında fazla bir şey bilmiyorsunuzdur.
Akhorus

4
Bunu yapan bir çevrimiçi hizmet var: algoritmaia.com/algorithms/PetiteProgrammer/…
Benny Neugebauer

Yanıtlar:


99

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 defanahtar kelimeye sahip olmasıdır . Dolayısıyla def, Python'da 1000x ve defRuby'de 100x gördüyse , yine de Python diyebilir putsveend 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)

1
Ayrıca forum yazılımında kullanmam gerekiyor. Bayes filtrelemesiyle ilgili ipucu için teşekkürler.
João Matos

12
NLP dersimde buna benzer bir şey yaptım ama bir adım daha ileri götürdük. Tek bir kelimenin frekanslarına bakmayı sevmiyorsunuz , fakat kelime çiftleri ve üçlüleri. Örneğin, "genel" birçok dilde bir anahtar kelime olabilir, ancak "genel statik boşluk" C # için daha yaygındır. Üçlü bulunamazsa, önce 2'ye ve ardından 1'e
düşersiniz

1
Kelimeleri nereye böldüğünüzü de düşünmek isteyebilirsiniz. PHP'de, değişkenler ile başlamak $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.
mpen

2
Evet. Bölmeyi hiç önlemenin bir yolu ngram kullanmaktır: her n uzunlukta alt dizeyi alırsınız. Örneğin 5 gramlık "puts foo", "puts" "uts f", "ts fo" ve "s foo" dur. Bu strateji tuhaf görünebilir ama düşündüğünüzden daha iyi işliyor, sadece bir insanın sorunu nasıl çözeceği değil. Hangi yöntemin daha iyi çalıştığına karar vermek için her ikisini de test etmeniz gerekecek ...
Jules

2
Yine de bazı dillerin çok az sözdizimi vardır. Ayrıca ortak değişken adlarının dilin anahtar kelimelerine hakim olacağını tahmin ediyorum. Temel olarak, eğitim verilerinizde Macarca değişken adlar ve yorumlar içeren bir Macar tarafından yazılmış bir C kodunuz varsa, içinde Macarca olan diğer herhangi bir kaynak muhtemelen "benzer" olarak belirlenecektir.
üçlü

26

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


4
Bu çözümlerin ikisini de inceledim ve hiçbiri tam olarak sorulan şeyi yapmayacak. Dili belirlemek için esas olarak dosya uzantılarına bakarlar, bu nedenle uzantıdan bir ipucu olmadan bir parçacığı inceleyemezler.
Hawkee

5
Github'un yaklaşımı artık bir Bayes sınıflandırıcısı da içeriyor. Öncelikle dosya uzantısına göre bir dil adayını algılar, ancak bir dosya uzantısı birden çok adayla eşleştiğinde (ör. ".H" -> C, C ++, ObjC), giriş kodu örneğini belirtecek ve önceden eğitilmiş bir kümeye göre sınıflandıracaktır. veri. Github sürümü, uzantıya da bakmadan her zaman kodu taramaya zorlanabilir.
Benzi



5

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.


26
Pekala, birkaç dil mümkünse, dedektör tüm olası adayları verebilir.
Steven Haryanto

Ya da eşleşen ilkini verebilir. Gerçek dünyadaki kullanım durumu sözdizimi vurgulama gibi bir şeyse, o zaman gerçekten bir fark yaratmaz. Bu, eşleşen dillerden herhangi birinin kodun doğru şekilde vurgulanmasına neden olacağı anlamına gelir.
jonschlinkert

5

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.


Highlight.js'ye dahil edilen dil verileri, vurgulama için gereken değerlerle sınırlıdır ve bu da dil algılama için oldukça yetersizdir (özellikle küçük miktarlarda kod için).
Adam Kennedy

İyi olduğunu düşünüyorum, bu kemanla kontrol et jsfiddle.net/3tgjnz10
sebilasse

4

İ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...

3
Sorun, bu anahtar kelimelerin değişken adları olarak veya dizeler halinde herhangi bir dilde görünmeye devam edebilmesidir. Bu ve kullanılan anahtar kelimelerde çok fazla örtüşme var. Bir anahtar kelimeye bakmaktan daha fazlasını yapmanız gerekir.
mpen

2

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.


Tüm diller bir BNF tarafından tanımlanamaz bile. Anahtar kelimeleri yeniden tanımlamanıza ve makrolar oluşturmanıza izin verilirse, bu çok daha zor hale gelir. Alså bir parçadan bahsederken, bir BNF'ye karşı daha zor ve hataya daha yatkın olan kısmi eşleştirme yapmanız gerekir.

2

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.


1

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.


1
Daha fazla incelendiğinde, güzel görünebilir, aslında dili algılamaz, ancak her öğenin sözdizimine göre vurgular.
Hawkee


1

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.


0

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.


0

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:

  • fonksiyon tanımları
  • değişken bildirimler
  • sınıf bildirimleri
  • yorumlar
  • döngüler için
  • Döngüler sırasında
  • ifadeleri yazdır

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.


0

İ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.


0

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 Gemfileuygulama 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-devve tekrar deneyin)

(Bir yapmanız gerekebilir sudo apt-get updateveya sudo apt-get install makeveya sudo apt-get install build-essentialyukarı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.tplancak 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 --breakdownuygulamanı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.


0

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 defbir işlevi tanımlamak için anahtar sözcüğü kullanın. Bu, bazı sorunlara neden olabilir, çünkü Ruby ayrıca defbir 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 endbir 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 endde, 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ı.


0

Rastgele karıştırıcıyı şöyle ayarlayın

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

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/

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.