web-dev-qa-db-ja.com

スニペットからプログラミング言語を検出する

コードのスニペットで使用されているプログラミング言語を検出する最良の方法は何でしょうか?

110
João Matos

スパムフィルターで使用される方法は非常にうまく機能すると思います。スニペットを単語に分割します。次に、これらの単語の出現を既知のスニペットと比較し、興味のあるすべての言語について、このスニペットが言語Xで記述されている確率を計算します。

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

基本的なメカニズムがあれば、新しい言語を追加するのは非常に簡単です。新しい言語でいくつかのスニペットを使用して検出器をトレーニングするだけです(オープンソースプロジェクトにフィードすることができます)。これにより、「システム」がC#スニペットに表示され、「プット」がRubyスニペットに表示される可能性が高いことがわかります。

実際にこの方法を使用して、フォーラムソフトウェアのコードスニペットに言語検出を追加しました。あいまいな場合を除き、100%動作しました。

print "Hello"

コードを見つけましょう。

コードが見つからなかったため、新しいコードを作成しました。それは少し単純ですが、私のテストでは機能します。現在、それをはるかに多くPythonコードよりもRubyコードよりも多くの場合、このコードは、

def foo
   puts "hi"
end

is Python code(実際にはRubyですが)。これは、Pythonにはdefキーワードもあるためです。 def in Pythonおよび100x def in Rubyそれから、まだPython = putsendは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)
98
Jules

他人が解決した言語検出:

Ohlohのアプローチ: https://github.com/blackducksw/ohcount/

Githubのアプローチ: https://github.com/github/linguist

26
nisc

ここにいくつかの有用な資料があります: http://alexgorbatchev.com/wiki/SyntaxHighlighter 。アレックスは、多数の異なる言語を解析する方法と、主要な構文要素が何であるかを考え出すのに多くの時間を費やしました。

7
Steve

Guesslangは可能な解決策です。

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

SourceClassifierもあります。

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

ブログ記事で特定できないコードを見つけた後、この問題に興味を持ちました。この質問は「プログラミング言語を特定する」ための最初の検索ヒットであったため、この回答を追加します。

6
ElectricWarr

別の方法は highlight.js を使用することです。これは構文の強調表示を実行しますが、強調表示プロセスの成功率を使用して言語を識別します。原則として、シンタックスハイライターコードベースは同じ方法で使用できますが、highlight.jsの良いところは、言語検出が機能と見なされ、 テスト目的で使用 であるということです。

UPDATE:これを試してみましたが、うまくいきませんでした。圧縮されたJavaScriptは完全に混乱させました。つまり、トークナイザーは空白文字に敏感です。一般に、ハイライトヒットを数えるだけでは、それほど信頼できるとは思えません。より強力なパーサー、またはおそらく一致しないセクションカウントがより適切に機能する場合があります。

5
Andy Jackson

それは非常に難しく、時には不可能です。この短いスニペットはどの言語ですか?

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

(ヒント:いくつかあります。)

さまざまな言語を分析し、キーワードの頻度分析を使用して決定しようとすることができます。特定のキーワードのセットがテキスト内の特定の頻度で発生する場合、言語はJavaなど)である可能性があります。ただし、たとえば、Javaのキーワードと同じ名前のCの変数、および周波数分析はだまされます。

複雑さを一段とすれば、構造を探すことができます。特定のキーワードが常に別のキーワードの後に​​来る場合、より多くの手がかりが得られます。しかし、設計と実装もはるかに困難になります。

5
user14070

まず、言語の特定のキーワークを見つけようとします。

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

それはあなたが持っているスニペットのタイプに依存しますが、一連のトークナイザーを実行して、どの言語のBNFが有効であるかを確認します。

2

これが必要だったので、自分で作成しました。 https://github.com/bertyhell/CodeClassifier

正しいフォルダーにトレーニングファイルを追加することで、非常に簡単に拡張できます。 C#で記述されています。しかし、コードは他の言語に簡単に変換されると思います。

1
Berty

素敵なパズル。

すべての言語を検出することは不可能だと思います。ただし、キートークンでトリガーできます。 (特定の予約語とよく使用される文字の組み合わせ)。

ベンには、同様の構文を持つ言語がたくさんあります。そのため、スニペットのサイズに依存します。

1
Toon Krijthe

Prettifyは、プログラミング言語を検出する大丈夫なJavascriptパッケージです。

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

これは主に構文ハイライターですが、おそらくスニペットから言語を検出する目的で検出部分を抽出する方法があります。

1
Hawkee

ランダムスクランブラーを次のように設定します

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
Rakesh

言語間の最大の違いはその構造にあると思います。したがって、私の考えは、すべての言語の特定の共通要素を見て、それらがどのように異なるかを確認することです。たとえば、正規表現を使用して、次のようなものを選択できます。

  • 関数定義
  • 変数宣言
  • クラス宣言
  • コメント
  • forループ
  • whileループ
  • 文を印刷する

そしておそらく、ほとんどの言語が持つべき他のいくつかのこと。次に、ポイントシステムを使用します。正規表現が見つかった場合、要素ごとに最大1ポイントを付与します。明らかに、一部の言語はまったく同じ構文を使用します(forループはfor(int i=0; i<x; ++i)のように記述されることが多いため、複数の言語がそれぞれ同じことに対してポイントを獲得できますが、少なくとも、完全に異なる言語)。それらのいくつかは、ボード全体で0を記録する場合があります(スニペットには関数がまったく含まれていません)。

これをジュールのソリューションと組み合わせると、かなりうまく機能するはずです。多分余分なポイントのキーワードの頻度を探します。

0
mpen

私が遭遇した最良の解決策は、RubyアプリのRailsで linguist gem を使用することです。これは特定の方法の一種ですが、機能します。これは@niscによって上で言及されましたが、それを使用するための正確な手順を教えます。 (次のコマンドラインコマンドの一部は、ubuntuに固有ですが、他のOSに簡単に変換する必要があります)

一時的にいじっても構わないRailsアプリがある場合は、その中に新しいファイルを作成して、問題のコードスニペットを挿入します。 (Railsがインストールされていない場合、良いガイド here がありますが、ubuntuには this をお勧めします。Rails new <name-your-app-dir>を実行し、そのディレクトリにcdします。Railsアプリを実行するために必要なものはすべてそこにあります)。

これを使用するRailsアプリを作成したら、gem 'github-linguist'をGemfileに追加します(文字列では、アプリディレクトリでGemfileと呼ばれ、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を実行できます。

特にRailsをまだ持っていない場合は、多くの余分な作業のように思えますが、これらの手順を実行しても、実際にRailsについて何も知る必要はありません。ファイル/コードスニペットの言語を検出するより良い方法。

0
StephanieS

単一のスニペットに基づいて、スニペットがどの言語にあるかを特定できる単一のソリューションはないと考えています。キーワードprintを使用します。それは、それぞれが異なる目的のためであり、異なる構文を持つ任意の数の言語で現れる可能性があります。

アドバイスがあります。私は現在、プログラミング言語を識別するために使用できるウェブサイト用の小さなコードを書いています。他のほとんどの投稿と同様に、巨大の範囲のプログラミング言語が存在する可能性があります。これは聞いたことがないだけで、すべてを説明することはできません。

私がやったことは、キーワードの選択によって各言語を識別できるということです。たとえば、Pythonはさまざまな方法で識別できます。おそらく言語固有の '特性'を選択する方が簡単です。Pythonでは、コロンを使用する特性を選択します一連のステートメントを開始しますが、これはかなりユニークな特性であると思います(間違っている場合は修正してください)。

私の例では、ステートメントセットを開始するコロンが見つからない場合、別の可能な特性に移動します。たとえば、defキーワードを使用して関数を定義します。 Rubyもキーワードdefを使用して関数を定義するため、2つの(PythonとRuby)を区別する鍵はさまざまなレベルを使用することですRubyキーワードendを使用して関数を終了しますが、Pythonには終了するものがありません関数であり、インデントを解除するだけですが、そこに行きたくありませんが、endもLuaである可能性があります。

プログラミング言語は単純にオーバーレイしすぎていることがわかります。ある言語のキーワードになる可能性のあるキーワードは、別の言語のキーワードになることがあります。 Javaのpublic static void main(String[] args)のように、よく一緒に使用されるキーワードの組み合わせを使用すると、これらの問題を解消できます。

すでに述べたように、あなたの一番のチャンスは、比較的ユニークなキーワードまたはキーワードのセットを探して互いに区別することです。そして、あなたがそれを間違えたら、少なくともあなたは行った。

0
William Lee

これを達成する簡単な方法はないと思います。特定の言語/言語のクラスに固有のシンボル/共通キーワードのリストを生成します(たとえば、Cスタイル言語の波括弧、BASIC言語のDimおよびSubキーワード、Pythonのdefキーワード、関数型言語のletキーワード) 。その後、基本的な構文機能を使用してさらに絞り込むことができる場合があります。

0
Noldorin

面白い。さまざまな形式のテキストを認識する同様のタスクがあります。 YAML、JSON、XML、またはJavaプロパティ?たとえば、構文エラーがあっても、JSONとXMLを区別して自信を持って伝える必要があります。

問題をどのようにモデル化するかが重要です。マークが言ったように、単一単語のトークン化は必要ですが、おそらく十分ではありません。バイグラム、またはトライグラムさえ必要です。しかし、私たちはプログラミング言語を検討していることを知っていれば、そこからさらに先に進むことができると思います。ほとんどすべてのプログラミング言語には、2つの固有のタイプのトークン-symbolsおよびkeywordsがあります。シンボルは、比較的簡単に認識できます(一部のシンボルは、言語の一部ではないリテラルである場合があります)。次に、シンボルのバイグラムまたはトライグラムは、シンボルの周囲の一意の構文構造を取得します。トレーニングセットが大きく、十分に多様な場合、キーワードも簡単なターゲットです。有用な機能は、可能なキーワードを取り巻くバイグラムです。トークンのもう1つの興味深いタイプは、whitespaceです。実際、通常の方法で空白でトークン化すると、この情報は失われます。プログラミング言語の分析では、構文トークンに関する有用な情報が含まれる可能性があるため、空白トークンを保持します。

最後に、ランダムフォレストのような分類子を選択した場合、githubをクロールし、すべてのパブリックソースコードを収集します。ほとんどのソースコードファイルは、ファイルサフィックスによってラベル付けできます。ファイルごとに、空の行でさまざまなサイズのスニペットにランダムに分割します。次に、ラベル付きスニペットを使用して機能を抽出し、分類器をトレーニングします。トレーニングが終了すると、分類器の精度と再現性をテストできます。

0
neurite