Cách tốt nhất để phát hiện ngôn ngữ lập trình nào được sử dụng trong một đoạn mã là gì?
Cách tốt nhất để phát hiện ngôn ngữ lập trình nào được sử dụng trong một đoạn mã là gì?
Câu trả lời:
Tôi nghĩ rằng phương pháp được sử dụng trong bộ lọc thư rác sẽ hoạt động rất tốt. Bạn chia đoạn mã thành các từ. Sau đó, bạn so sánh các lần xuất hiện của những từ này với các đoạn mã đã biết và tính xác suất đoạn mã này được viết bằng ngôn ngữ X cho mọi ngôn ngữ bạn quan tâm.
http://en.wikipedia.org/wiki/Bayesian_spam_filtering
Nếu bạn có cơ chế cơ bản thì rất dễ dàng thêm các ngôn ngữ mới: chỉ cần đào tạo trình dò tìm một vài đoạn mã bằng ngôn ngữ mới (bạn có thể cung cấp cho nó một dự án nguồn mở). Bằng cách này, nó biết rằng "Hệ thống" có khả năng xuất hiện trong các đoạn mã C # và "đặt" trong các đoạn mã Ruby.
Tôi đã thực sự sử dụng phương pháp này để thêm tính năng phát hiện ngôn ngữ vào các đoạn mã cho phần mềm diễn đàn. Nó hoạt động 100% thời gian, trừ những trường hợp không rõ ràng:
print "Hello"
Hãy để tôi tìm mã.
Tôi không thể tìm thấy mã nên tôi đã tạo một mã mới. Nó hơi đơn giản nhưng nó hoạt động cho các thử nghiệm của tôi. Hiện tại nếu bạn cung cấp cho nó nhiều mã Python hơn mã Ruby, nó có thể nói rằng mã này:
def foo
puts "hi"
end
là mã Python (mặc dù nó thực sự là Ruby). Điều này là do Python cũng có một def
từ khóa. Vì vậy, nếu nó đã thấy 1000x def
trong Python và 100x def
trong Ruby thì nó vẫn có thể nói là Python mặc dù puts
và end
là Ruby cụ thể. Bạn có thể khắc phục điều này bằng cách theo dõi các từ được nhìn thấy cho mỗi ngôn ngữ và chia cho nó ở đâu đó (hoặc bằng cách cung cấp cho nó lượng mã bằng nhau trong mỗi ngôn ngữ).
Tôi hy vọng nó sẽ giúp bạn:
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)
$
, vì vậy có thể bạn không nên tách theo giới hạn từ, vì biến $
này nên gắn liền với biến. Các nhà khai thác thích =>
và :=
nên được gắn với nhau như một mã thông báo duy nhất, nhưng OTH có lẽ bạn nên tách ra {
vì chúng luôn đứng riêng.
Phát hiện ngôn ngữ do những người khác giải quyết:
Cách tiếp cận của Ohloh: https://github.com/blackducksw/ohcount/
Cách tiếp cận của Github: https://github.com/github/linguist
Bạn có thể tìm thấy một số tài liệu hữu ích ở đây: http://alexgorbatchev.com/wiki/SyntaxHighlighter . Alex đã dành rất nhiều thời gian để tìm ra cách phân tích cú pháp một số lượng lớn các ngôn ngữ khác nhau và các phần tử cú pháp chính là gì.
Guesslang là một giải pháp khả thi:
http://guesslang.readthedocs.io/en/latest/index.html
Ngoài ra còn có SourceClassifier:
https://github.com/chrislo/sourceclassifier/tree/master
Tôi bắt đầu quan tâm đến vấn đề này sau khi tìm thấy một số mã trong một bài báo trên blog mà tôi không thể xác định được. Thêm câu trả lời này vì câu hỏi này là lần truy cập tìm kiếm đầu tiên cho "xác định ngôn ngữ lập trình".
Nó rất khó và đôi khi là không thể. Đoạn trích ngắn này từ ngôn ngữ nào?
int i = 5;
int k = 0;
for (int j = 100 ; j > i ; i++) {
j = j + 1000 / i;
k = k + i * j;
}
(Gợi ý: Nó có thể là bất kỳ một trong số nhiều.)
Bạn có thể cố gắng phân tích các ngôn ngữ khác nhau và cố gắng quyết định bằng cách sử dụng phân tích tần suất của từ khóa. Nếu một số tập hợp từ khóa nhất định xuất hiện với tần suất nhất định trong một văn bản thì có khả năng là ngôn ngữ là Java, v.v. Nhưng tôi không nghĩ rằng bạn sẽ nhận được bất kỳ thứ gì hoàn toàn là bằng chứng đánh lừa, vì bạn có thể đặt tên cho một biến trong C cùng tên chẳng hạn như một từ khóa trong Java, và phân tích tần suất sẽ bị đánh lừa.
Nếu bạn đưa nó lên một mức độ phức tạp, bạn có thể tìm kiếm các cấu trúc, nếu một từ khóa nhất định luôn đi sau từ khóa khác, điều đó sẽ giúp bạn có thêm manh mối. Nhưng nó cũng sẽ khó hơn nhiều để thiết kế và thực hiện.
Một giải pháp thay thế là sử dụng highlight.js , thực hiện đánh dấu cú pháp nhưng sử dụng tỷ lệ thành công của quá trình đánh dấu để xác định ngôn ngữ. Về nguyên tắc, bất kỳ cơ sở mã của trình đánh dấu cú pháp nào cũng có thể được sử dụng theo cách tương tự, nhưng điều thú vị về highlight.js là tính năng phát hiện ngôn ngữ được coi là một tính năng và được sử dụng cho mục đích thử nghiệm .
CẬP NHẬT: Tôi đã thử điều này và nó không hoạt động tốt. JavaScript nén hoàn toàn nhầm lẫn nó, tức là tokenizer nhạy cảm với khoảng trắng. Nói chung, việc chỉ đếm số lượt truy cập nổi bật có vẻ không đáng tin cậy lắm. Trình phân tích cú pháp mạnh hơn hoặc có thể là số lượng phần chưa từng có, có thể hoạt động tốt hơn.
Đầu tiên, tôi sẽ cố gắng tìm các keyworks cụ thể của một ngôn ngữ, ví dụ:
"package, class, implements "=> JAVA
"<?php " => PHP
"include main fopen strcmp stdout "=>C
"cout"=> C++
etc...
Nó sẽ phụ thuộc vào loại đoạn mã bạn có, nhưng tôi sẽ chạy nó qua một loạt các trình mã hóa và xem BNF của ngôn ngữ nào mà nó tạo ra là hợp lệ.
Câu đố hay.
Tôi nghĩ rằng nó là bất khả thi để phát hiện tất cả các ngôn ngữ. Nhưng bạn có thể kích hoạt các mã thông báo chính. (các từ dành riêng nhất định và các tổ hợp ký tự thường được sử dụng).
Ben có rất nhiều ngôn ngữ với cú pháp tương tự. Vì vậy, nó phụ thuộc vào kích thước của đoạn mã.
Prettify là một gói Javascript thực hiện tốt công việc phát hiện ngôn ngữ lập trình:
http://code.google.com/p/google-code-prettify/
Nó chủ yếu là một công cụ đánh dấu cú pháp, nhưng có lẽ có một cách để trích xuất phần phát hiện nhằm mục đích phát hiện ngôn ngữ từ một đoạn mã.
Tôi cần cái này nên tôi đã tạo ra cái của riêng mình. https://github.com/bertyhell/CodeClassifier
Nó có thể mở rộng rất dễ dàng bằng cách thêm tệp đào tạo vào đúng thư mục. Được viết bằng c #. Nhưng tôi tưởng tượng mã dễ dàng được chuyển đổi sang bất kỳ ngôn ngữ nào khác.
Tôi sẽ không nghĩ rằng sẽ có một cách dễ dàng để đạt được điều này. Tôi có thể sẽ tạo danh sách các ký hiệu / từ khóa chung duy nhất cho một số ngôn ngữ / lớp ngôn ngữ nhất định (ví dụ: dấu ngoặc nhọn cho ngôn ngữ kiểu C, từ khóa Dim và Sub cho ngôn ngữ BASIC, từ khóa def cho Python, từ khóa let cho ngôn ngữ chức năng) . Sau đó, bạn có thể sử dụng các tính năng cú pháp cơ bản để thu hẹp nó hơn nữa.
Tôi nghĩ sự khác biệt lớn nhất giữa các ngôn ngữ là cấu trúc của nó. Vì vậy, ý tưởng của tôi là xem xét các yếu tố chung nhất định trên tất cả các ngôn ngữ và xem chúng khác nhau như thế nào. Ví dụ: bạn có thể sử dụng regex để chọn những thứ như:
Và có thể một vài thứ khác mà hầu hết các ngôn ngữ phải có. Sau đó, sử dụng một hệ thống điểm. Thưởng nhiều nhất 1 điểm cho mỗi phần tử nếu tìm thấy regex. Rõ ràng, một số ngôn ngữ sẽ sử dụng cùng một cú pháp (vòng lặp for thường được viết giống như for(int i=0; i<x; ++i)
vậy nên nhiều ngôn ngữ có thể ghi một điểm cho cùng một thứ, nhưng ít nhất bạn đang giảm khả năng nó là một ngôn ngữ hoàn toàn khác). Một số người trong số họ có thể đạt điểm 0 trên bảng (ví dụ: đoạn mã không chứa một chức năng nào cả) nhưng điều đó hoàn toàn ổn.
Kết hợp điều này với giải pháp của Jules, và nó sẽ hoạt động khá tốt. Cũng có thể tìm kiếm tần suất của các từ khóa để có thêm một điểm.
Hấp dẫn. Tôi có một nhiệm vụ tương tự là nhận dạng văn bản ở các định dạng khác nhau. Thuộc tính YAML, JSON, XML hoặc Java? Ngay cả với các lỗi cú pháp, chẳng hạn, tôi nên tự tin phân biệt JSON với XML.
Tôi cho rằng cách chúng tôi mô hình vấn đề là rất quan trọng. Như Mark đã nói, mã hóa một từ là cần thiết nhưng có thể là chưa đủ. Chúng ta sẽ cần đến bigram, hoặc thậm chí là bát quái. Nhưng tôi nghĩ chúng ta có thể tiến xa hơn từ đó khi biết rằng chúng ta đang xem xét các ngôn ngữ lập trình. Tôi nhận thấy rằng hầu hết mọi ngôn ngữ lập trình đều có hai loại mã thông báo duy nhất - biểu tượng và từ khóa . Các ký hiệu tương đối dễ dàng (một số ký hiệu có thể là chữ không phải là một phần của ngôn ngữ) để nhận ra. Sau đó, bigram hoặc bát quái của các biểu tượng sẽ chọn các cấu trúc cú pháp độc đáo xung quanh các biểu tượng. Từ khóa là một mục tiêu dễ dàng khác nếu tập hợp đào tạo đủ lớn và đa dạng. Một tính năng hữu ích có thể là bigram xung quanh các từ khóa có thể. Một loại mã thông báo thú vị khác là khoảng trắng. Trên thực tế, nếu chúng ta mã hóa theo cách thông thường bằng khoảng trắng, chúng ta sẽ mất thông tin này. Tôi muốn nói, để phân tích ngôn ngữ lập trình, chúng tôi giữ các mã thông báo khoảng trắng vì điều này có thể mang thông tin hữu ích về cấu trúc cú pháp.
Cuối cùng nếu tôi chọn một bộ phân loại như rừng ngẫu nhiên, tôi sẽ thu thập dữ liệu github và thu thập tất cả mã nguồn công khai. Hầu hết tệp mã nguồn có thể được gắn nhãn bằng hậu tố tệp. Đối với mỗi tệp, tôi sẽ chia ngẫu nhiên nó ở các dòng trống thành các đoạn mã có kích thước khác nhau. Sau đó, tôi sẽ trích xuất các tính năng và đào tạo bộ phân loại bằng cách sử dụng các đoạn mã được gắn nhãn. Sau khi huấn luyện xong, bộ phân loại có thể được kiểm tra độ chính xác và thu hồi.
Giải pháp tốt nhất mà tôi đã thử là sử dụng viên ngọc nhà ngôn ngữ học trong ứng dụng Ruby on Rails. Đó là một cách cụ thể để làm điều đó, nhưng nó hoạt động. Điều này đã được đề cập ở trên bởi @nisc nhưng tôi sẽ cho bạn biết các bước chính xác của tôi để sử dụng nó. (Một số lệnh dòng lệnh sau dành riêng cho ubuntu nhưng nên dễ dàng dịch sang hệ điều hành khác)
Nếu bạn có bất kỳ ứng dụng rails nào mà bạn tạm thời không bận tâm, hãy tạo một tệp mới trong đó để chèn đoạn mã của bạn được đề cập. (Nếu bạn chưa cài đặt rails, có một hướng dẫn tốt ở đây mặc dù đối với ubuntu, tôi khuyên bạn nên làm điều này . Sau đó chạy rails new <name-your-app-dir>
và cd vào thư mục đó. Mọi thứ bạn cần để chạy ứng dụng rails đã có sẵn).
Sau khi bạn có một ứng dụng rails để sử dụng, hãy thêm gem 'github-linguist'
vào Gemfile của bạn (nghĩa đen chỉ được gọi Gemfile
trong thư mục ứng dụng của bạn, không có số máy lẻ).
Sau đó cài đặt ruby-dev ( sudo apt-get install ruby-dev
)
Sau đó cài đặt cmake ( sudo apt-get install cmake
)
Bây giờ bạn có thể chạy gem install github-linguist
(nếu bạn gặp lỗi cho biết yêu cầu icu, hãy làm sudo apt-get install libicu-dev
và thử lại)
(Bạn có thể cần phải làm một sudo apt-get update
hoặc sudo apt-get install make
hoặc sudo apt-get install build-essential
nếu những điều trên không hiệu quả)
Bây giờ mọi thứ đã được thiết lập. Bây giờ bạn có thể sử dụng điều này bất kỳ lúc nào bạn muốn kiểm tra các đoạn mã. Trong trình soạn thảo văn bản, hãy mở tệp bạn đã tạo để chèn đoạn mã của mình (giả sử như vậy app/test.tpl
nhưng nếu biết phần mở rộng của đoạn mã, hãy sử dụng tệp đó thay vì sử dụng .tpl
. Nếu bạn không biết phần mở rộng, đừng sử dụng ). Bây giờ, hãy dán đoạn mã của bạn vào tệp này. Đi tới dòng lệnh và chạy bundle install
(phải nằm trong thư mục ứng dụng của bạn). Sau đó chạy linguist app/test.tpl
(tổng quát hơn linguist <path-to-code-snippet-file>
). Nó sẽ cho bạn biết loại, kiểu kịch câm và ngôn ngữ. Đối với nhiều tệp (hoặc để sử dụng chung với ứng dụng ruby / rails), bạn có thể chạy bundle exec linguist --breakdown
trong thư mục ứng dụng của mình.
Có vẻ như rất nhiều công việc bổ sung, đặc biệt nếu bạn chưa có đường ray, nhưng bạn thực sự không cần biết BẤT CỨ ĐIỀU GÌ về đường ray nếu bạn làm theo các bước sau và tôi thực sự không tìm ra cách tốt hơn để phát hiện ngôn ngữ của tệp / đoạn mã.
Tôi tin rằng không có giải pháp duy nhất nào có thể xác định được ngôn ngữ của một đoạn mã, chỉ dựa trên đoạn mã đó. Lấy từ khóa print
. Nó có thể xuất hiện ở bất kỳ số ngôn ngữ nào, mỗi ngôn ngữ dành cho các mục đích khác nhau và có cú pháp khác nhau.
Tôi có một số lời khuyên. Tôi hiện đang viết một đoạn mã nhỏ cho trang web của mình có thể được sử dụng để xác định ngôn ngữ lập trình. Giống như hầu hết các bài viết khác, có thể có rất nhiều ngôn ngữ lập trình mà bạn chỉ đơn giản là chưa nghe, bạn không thể giải thích tất cả.
Những gì tôi đã làm là mỗi ngôn ngữ có thể được xác định bằng một số từ khóa. Ví dụ, Python có thể được xác định theo một số cách. Có lẽ sẽ dễ dàng hơn nếu bạn chọn các 'đặc điểm' chắc chắn cũng là duy nhất cho ngôn ngữ. Đối với Python, tôi chọn đặc điểm sử dụng dấu hai chấm để bắt đầu một tập hợp các câu lệnh, mà tôi tin rằng đây là một đặc điểm khá độc đáo (hãy sửa cho tôi nếu tôi sai).
Nếu, trong ví dụ của tôi, bạn không thể tìm thấy dấu hai chấm để bắt đầu một bộ câu lệnh, thì hãy chuyển sang một đặc điểm có thể có khác, giả sử sử dụng def
từ khóa để xác định một hàm. Bây giờ điều này có thể gây ra một số vấn đề, vì Ruby cũng sử dụng từ khóa def
để định nghĩa một hàm. Chìa khóa để phân biệt hai (Python và Ruby) là sử dụng các cấp độ lọc khác nhau để có được kết quả phù hợp nhất. Ruby sử dụng từ khóa end
để kết thúc một hàm, trong khi Python không có bất cứ thứ gì để kết thúc một hàm, chỉ là một đoạn thụt lề nhưng bạn không muốn đến đó. Nhưng một lần nữa, end
cũng có thể là Lua, một ngôn ngữ lập trình khác để thêm vào hỗn hợp.
Bạn có thể thấy rằng các ngôn ngữ lập trình chỉ đơn giản là phủ quá nhiều. Một từ khóa có thể là từ khóa ở một ngôn ngữ có thể trở thành từ khóa ở ngôn ngữ khác. Sử dụng kết hợp các từ khóa thường đi cùng nhau, như Java public static void main(String[] args)
sẽ giúp loại bỏ những vấn đề đó.
Như tôi đã nói, cơ hội tốt nhất của bạn là tìm kiếm các từ khóa hoặc bộ từ khóa tương đối độc đáo để tách biệt từ khóa này với từ khóa khác. Và, nếu bạn hiểu sai, ít nhất bạn đã có một cơ hội.
Trang web này có vẻ khá tốt trong việc xác định ngôn ngữ, nếu bạn muốn một cách nhanh chóng để dán một đoạn mã vào biểu mẫu web, thay vì thực hiện theo chương trình: http://dpaste.com/