从代码片段检测编程语言

检测代码片段中使用的编程语言的最佳方法是什么?

113395 次浏览

这将取决于代码片段的类型,但是我会通过一系列标记器来运行它,看看它对哪种语言的 BNF 是有效的。

首先,我会尝试找出一种语言的具体关键工作,例如。

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

我认为在垃圾邮件过滤器中使用的方法将非常有效。你把片段分割成文字。然后将这些单词的出现情况与已知代码片段进行比较,并计算每种感兴趣的语言使用 X 语言编写该代码片段的可能性。

Http://en.wikipedia.org/wiki/bayesian_spam_filtering

如果有了基本的机制,那么添加新语言就非常容易: 只需用新语言中的一些片段来训练检测器(您可以向它提供一个开源项目)。通过这种方式,它了解到“ System”很可能出现在 C # 代码片段中,而“ put”出现在 Ruby 代码片段中。

实际上,我已经使用这种方法将语言检测添加到论坛软件的代码片段中。它在100% 的时间里都有效,除了在模棱两可的情况下:

print "Hello"

让我找到密码。

我找不到密码,所以我做了个新的。这有点简单,但对我的测试很有用。当前,如果你给它提供比 Ruby 更多的 Python 代码,它可能会说这段代码:

def foo
puts "hi"
end

是 Python 代码(尽管它实际上是 Ruby)。这是因为 Python 也有一个 def关键字。因此,如果它在 Python 中看到了1000xdef,在 Ruby 中看到了100xdef,那么即使 putsend是 Ruby 特有的,它仍然可以说是 Python。您可以通过跟踪每种语言所看到的单词并将其除以某处(或者通过在每种语言中向其提供相同数量的代码)来解决这个问题。

我希望这对你有所帮助:

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)

不错的拼图。

我认为不可能检测到所有的语言。但是可以触发密钥标记。(某些保留词和经常使用的字符组合)。

有很多语言都有类似的语法,所以这取决于代码片段的大小。

我不认为有什么简单的方法能做到这一点。我可能会生成特定语言/语言类别独有的符号/通用关键字列表(例如,C 风格语言的花括号,BASIC 语言的 Dim 和 Sub 关键字,Python 的 def 关键字,函数式语言的 let 关键字)。然后,您可以使用基本的语法特性来进一步缩小范围。

这是非常困难的,有时是不可能的。这个简短的片段来自哪种语言?

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

(提示: 它可以是几个中的任何一个。)

您可以尝试分析各种语言,并尝试使用关键字的频率分析来决定。如果特定的关键字集在文本中以特定的频率出现,那么这种语言很可能是 Java 等。但我不认为你会得到任何完全愚蠢的证明,例如,你可以在 C 中命名一个变量,与 Java 中的一个关键字同名,频率分析将被愚弄。

如果你把它的复杂性提高一个档次,你可以寻找结构,如果某个关键字总是在另一个之后,这将让你得到更多的线索。但设计和实现起来也会困难得多。

我认为语言之间最大的区别在于它的结构。因此,我的想法是研究所有语言中的某些共同元素,看看它们之间的差异。例如,您可以使用正则表达式来选择以下内容:

  • 函数定义
  • 变量声明
  • 类声明
  • 评论
  • 循环
  • While 循环
  • 打印报表

也许还有其他一些大多数语言应该具备的东西。那就用计分法。如果找到正则表达式,则每个元素最多只能得到1分。显然,有些语言会使用完全相同的语法(for 循环通常像 for(int i=0; i<x; ++i)一样编写,所以多种语言可以为同一个事物各得一分,但至少可以减少它成为一种完全不同的语言的可能性)。其中一些可能全部得0分(例如,代码片段根本不包含函数) ,但这完全没问题。

把这个和朱尔斯的解决方案结合起来应该会很有效。也许还可以查找关键字的频率,寻找一个额外的点。

其他人解决的语言检测:

Ohloh 的方法: https://github.com/blackducksw/ohcount/

Github 的方法: https://github.com/github/linguist

Prettify 是一个 Javascript 包,它能够很好地检测编程语言:

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

它主要是一个语法高亮显示器,但是可能有一种方法可以提取检测部分,以便从代码片段中检测语言。

另一种选择是使用 突出 Js,它执行语法突显,但使用突出显示过程的成功率来识别语言。原则上,任何语法高亮显示器代码库都可以以相同的方式使用,但是 highlight.js 的优点是语言检测被认为是一个特性,它是 用于测试目的

更新: 我试过了,但是效果不是很好。压缩后的 JavaScript 完全混淆了它,即标记器是空格敏感的。一般来说,仅仅计算高亮点击量似乎并不十分可靠。更强大的解析器,或者可能是无法匹配的节计数,可能会工作得更好。

有意思。我有一个类似的任务来识别不同格式的文本。YAML、 JSON、 XML 还是 Java 属性?例如,即使有语法错误,我也应该有把握地区分 JSON 和 XML。

我认为我们如何建模这个问题是至关重要的。正如 Mark 所说,单词标记化是必要的,但可能还不够。我们将需要双字母,甚至三角形。但是我认为我们可以更进一步,因为我们知道我们正在研究编程语言。我注意到,几乎任何编程语言都有两种独特的令牌类型—— 符号关键词。符号相对容易识别(有些符号可能是字面意思,而不是语言的一部分)。然后,符号的二元或三角形将获得围绕符号的独特语法结构。如果训练集足够大和多样化,关键字是另一个容易的目标。一个有用的特性可能是围绕可能的关键字的双字符。另一种有趣的令牌类型是 空格。实际上,如果我们以通常的方式使用空格进行标记,我们将丢失这个信息。我想说,为了分析编程语言,我们保留空格标记,因为它可能携带有关语法结构的有用信息。

最后,如果我选择一个类似于随机森林的分类器,我将抓取 github 并收集所有的公共源代码。大多数源代码文件可以用文件后缀标记。对于每个文件,我将以空行的形式将其随机分割为不同大小的片段。然后,我将提取这些特性,并使用带标签的代码片段训练分类器。训练完成后,分类器可以进行准确率召回率测试。

我需要这个,所以我创造了自己的。 Https://github.com/bertyhell/codeclassifier

通过在正确的文件夹中添加一个培训文件,它很容易扩展。 用 c # 编写的,但是我想代码可以很容易地转换成其他语言。

我遇到的最好的解决方案是在 RubyonRails 应用程序中使用 语言学宝石。这是一种特殊的方式,但是很有效。上面@nisc 提到过这一点,但是我会告诉你使用它的具体步骤。(下面的一些命令行命令是特定于 ubuntu 的,但是可以很容易地转换为其他操作系统的命令行命令)

如果您有任何 Rails 应用程序,您不介意暂时混乱,在其中创建一个新的文件,以插入您的代码片段的问题。(如果你没有安装 Rails,有一个很好的指南 给你,虽然对于 ubuntu 我推荐 这个。然后在该目录中运行 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 updatesudo apt-get install makesudo apt-get install build-essential)

现在一切都准备好了。现在,您可以在检查代码段的任何时候使用它。在文本编辑器中,打开用于插入代码段的文件(假设它是 app/test.tpl,但是如果知道代码段的扩展名,则使用它而不是 .tpl。如果你不知道分机号,就不要用它)。现在将代码片段粘贴到该文件中。转到命令行并运行 bundle install(必须在应用程序的目录中)。然后运行 linguist app/test.tpl(通常是 linguist <path-to-code-snippet-file>)。它会告诉你的类型,哑剧类型,和语言。对于多个文件(或者对于 Ruby/Rails 应用程序的一般使用) ,可以在应用程序的目录中运行 bundle exec linguist --breakdown

似乎有很多额外的工作要做,尤其是如果你还没有 Rails,但是如果你遵循这些步骤,你实际上并不需要了解 Rails 的任何东西,我真的没有找到一个更好的方法来检测文件/代码片段的语言。

我相信没有一个单一的解决方案可以仅仅基于这个单一的代码片段来识别代码片段所使用的语言。使用关键字 print。它可以出现在任意数量的语言中,每种语言都有不同的用途,并且具有不同的语法。

我确实有些建议。我目前正在为我的网站写一小段代码,可以用来识别编程语言。像大多数其他文章一样,可能有一系列 巨大编程语言,你只是没有听说过,你不能解释他们所有。

我所做的是,每种语言都可以通过选择关键字来识别。例如,可以通过多种方式识别 Python。如果你选择的“特征”对于语言来说也是独一无二的,那么这可能会更容易。对于 Python,我选择使用冒号来启动一组语句的 trait,我认为这是一个相当独特的 trait (如果我错了请纠正我)。

在我的例子中,如果您找不到一个冒号来启动一个语句集,那么可以转移到另一个可能的 trait,比方说使用 def关键字来定义一个函数。现在这可能会导致一些问题,因为 Ruby 也使用关键字 def来定义函数。区分两者(Python 和 Ruby)的关键是使用不同级别的过滤来获得最佳匹配。Ruby 使用关键字 end来完成一个函数,而 Python 没有任何东西来完成一个函数,只有一个去缩进,但是你不想去那里。但同样,end也可以是 Lua,这是另一种可以添加到混合编程语言中的语言。

您可以看到,编程语言只是覆盖了太多内容。一个关键字在一种语言中可能是一个关键字,而在另一种语言中可能是一个关键字。使用经常一起使用的关键字组合,比如 Java 的 public static void main(String[] args),有助于消除这些问题。

正如我已经说过的,您最好的机会是寻找相对独特的关键字或关键字集,以区分一个从其他。如果你做错了,至少你试过了。

设置随机扰码器

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;

Guesslang 是一个可能的解决方案:

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

还有 SourceClassfier:

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

在一篇博客文章中发现一些我无法识别的代码后,我开始对这个问题感兴趣。这个问题是“标识编程语言”的第一个搜索结果,因此添加了这个答案。

这个网站似乎非常善于识别语言,如果你想快速粘贴代码片段到 web 表单,而不是通过编程的方式: http://dpaste.com/