检测代码片段中使用哪种编程语言的最佳方法是什么?
17 回答
我认为垃圾邮件过滤器中使用的方法效果很好。您将片段分成单词。然后,您将这些单词的出现与已知片段进行比较,并计算该片段以您感兴趣的每种语言的语言 X 编写的概率。
http://en.wikipedia.org/wiki/Bayesian_spam_filtering
如果您有基本机制,那么添加新语言非常容易:只需使用新语言的一些片段训练检测器(您可以将其提供给开源项目)。通过这种方式,它知道“System”可能出现在 C# 代码片段中,而“puts”可能出现在 Ruby 代码片段中。
我实际上已经使用这种方法将语言检测添加到论坛软件的代码片段中。它在 100% 的时间内都有效,除了在模棱两可的情况下:
print "Hello"
让我找到代码。
我找不到代码,所以我做了一个新的。这有点简单,但它适用于我的测试。目前,如果你给它提供的 Python 代码比 Ruby 代码多得多,它可能会说这段代码:
def foo
puts "hi"
end
是 Python 代码(虽然它确实是 Ruby)。这是因为 Python 也有def
关键字。因此,如果它def
在 Python 中看到了 1000 倍def
,在 Ruby 中看到了 100 倍,那么它可能仍然会说 Pythonputs
并且end
是特定于 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)
别人解决的语言检测:
Ohloh的做法:https ://github.com/blackducksw/ohcount/
Github 的做法:https ://github.com/github/linguist
Guesslang 是一个可能的解决方案:
http://guesslang.readthedocs.io/en/latest/index.html
还有 SourceClassifier:
https://github.com/chrislo/sourceclassifier/tree/master
在我无法识别的博客文章中找到一些代码后,我对这个问题产生了兴趣。添加此答案,因为此问题是“识别编程语言”的第一次搜索命中。
另一种方法是使用highlight.js,它执行语法突出显示,但使用突出显示过程的成功率来识别语言。原则上,任何语法高亮代码库都可以以相同的方式使用,但是 highlight.js 的好处是语言检测被认为是一项功能并用于测试目的。
更新:我试过了,但效果不太好。压缩的 JavaScript 完全混淆了它,即标记器对空格敏感。一般来说,只计算亮点点击似乎不太可靠。一个更强大的解析器,或者可能是无与伦比的节数,可能会更好。
首先,我会尝试找到一种语言的特定keyworks,例如
"package, class, implements "=> JAVA
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...
这非常困难,有时甚至是不可能的。这个简短的片段来自哪种语言?
int i = 5;
int k = 0;
for (int j = 100 ; j > i ; i++) {
j = j + 1000 / i;
k = k + i * j;
}
(提示:它可以是几个中的任何一个。)
您可以尝试分析各种语言并尝试决定使用关键字的频率分析。如果某些关键字集在文本中以某些频率出现,则该语言很可能是 Java 等。但我认为您不会得到任何完全万无一失的东西,因为您可以将 C 中的变量命名为相同的名称作为Java中的关键字,频率分析会被愚弄。
如果您将其复杂性提高一个档次,则可以寻找结构,如果某个关键字总是在另一个关键字之后,那将为您提供更多线索。但它的设计和实施也将更加困难。
这取决于您拥有哪种类型的代码段,但我会通过一系列标记器运行它,并查看它针对哪种语言的 BNF 有效。
我需要这个,所以我创建了自己的。 https://github.com/bertyhell/CodeClassifier
通过在正确的文件夹中添加一个培训文件,它很容易扩展。用c#写的。但我想代码很容易转换为任何其他语言。
我遇到的最佳解决方案是在 Ruby on Rails 应用程序中使用linguist gem 。这是一种特定的方法,但它确实有效。@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 update
orsudo 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 的任何信息,而且我真的没有找到更好的方法来检测文件/代码片段的语言。
不错的拼图。
我认为检测所有语言是不可能的。但是您可以触发关键令牌。(某些保留字和常用字符组合)。
Ben 有很多具有相似语法的语言。所以这取决于片段的大小。
Prettify 是一个 Javascript 包,可以很好地检测编程语言:
http://code.google.com/p/google-code-prettify/
它主要是一个语法荧光笔,但可能有一种方法可以提取检测部分,以便从片段中检测语言。
我认为不会有一种简单的方法来实现这一点。我可能会生成某些语言/语言类别特有的符号/常用关键字列表(例如,C 风格语言的大括号,BASIC 语言的 Dim 和 Sub 关键字,Python 的 def 关键字,函数式语言的 let 关键字) . 然后,您可能能够使用基本语法功能来进一步缩小范围。
我认为语言之间最大的区别在于它的结构。所以我的想法是查看所有语言中的某些共同元素,看看它们有何不同。例如,您可以使用正则表达式来挑选以下内容:
- 函数定义
- 变量声明
- 类声明
- 评论
- for 循环
- while 循环
- 打印报表
也许还有其他一些大多数语言应该具备的东西。然后使用积分系统。如果找到正则表达式,每个元素最多奖励 1 分。显然,某些语言将使用完全相同的语法(for 循环通常写成这样for(int i=0; i<x; ++i)
,因此多种语言可以为同一事物各得一分,但至少您降低了它是完全不同语言的可能性)。其中一些可能全面得分为 0(例如,代码片段根本不包含函数),但这完全没问题。
将此与 Jules 的解决方案结合起来,它应该可以很好地工作。也许还可以寻找关键字的频率以获得额外的分数。
有趣的。我有一个类似的任务来识别不同格式的文本。YAML、JSON、XML 或 Java 属性?例如,即使存在语法错误,我也应该自信地将 JSON 与 XML 区分开来。
我认为我们如何为问题建模是至关重要的。正如 Mark 所说,单个词标记化是必要的,但可能还不够。我们将需要二元组,甚至三元组。但我认为我们可以从那里走得更远,因为我们正在研究编程语言。我注意到几乎所有编程语言都有两种独特的标记类型——符号和关键字。符号相对容易识别(某些符号可能是不属于语言的文字)。然后符号的二元组或三元组将在符号周围拾取独特的语法结构。如果训练集足够大且足够多样化,关键字是另一个简单的目标。一个有用的功能可能是围绕可能的关键字的二元组。另一种有趣的标记类型是空格. 实际上,如果我们以通常的方式通过空格进行标记,我们将丢失这些信息。我想说,为了分析编程语言,我们保留空白标记,因为这可能携带有关语法结构的有用信息。
最后,如果我选择像随机森林这样的分类器,我会爬 github 并收集所有公共源代码。大多数源代码文件都可以通过文件后缀来标记。对于每个文件,我会在空行处将其随机拆分为各种大小的片段。然后,我将提取特征并使用标记的片段训练分类器。训练完成后,可以测试分类器的精度和召回率。
如果您想要一种将代码段粘贴到 Web 表单中的快速方法,而不是以编程方式进行,那么该站点似乎非常擅长识别语言:http: //dpaste.com/
我相信没有单一的解决方案可以仅基于该单个片段来识别片段所使用的语言。取关键词print
。它可以以任意数量的语言出现,每种语言都有不同的用途,并且具有不同的语法。
我确实有一些建议。我目前正在为我的网站编写一小段代码,可用于识别编程语言。像大多数其他帖子一样,可能有大量您根本没有听说过的编程语言,您无法解释所有这些。
我所做的是,每种语言都可以通过选择的关键字来识别。例如,可以通过多种方式识别 Python。如果您选择当然也是该语言独有的“特征”,这可能会更容易。对于 Python,我选择使用冒号开始一组语句的特性,我认为这是一个相当独特的特性(如果我错了,请纠正我)。
如果在我的示例中,您找不到冒号来启动语句集,那么请转到另一个可能的特征,假设使用def
关键字来定义函数。现在这可能会导致一些问题,因为 Ruby 也使用关键字def
来定义函数。区分这两者(Python 和 Ruby)的关键是使用不同级别的过滤来获得最佳匹配。Ruby 使用关键字end
来完成一个函数,而 Python 没有任何东西来完成一个函数,只是一个 de-indent 但你不想去那里。但同样,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;