Как я могу определить язык программирования фрагмента?
У меня есть строка, содержащая некоторый текст. Текст может быть или не быть кодом. Используя Github's Linguist, я смог определить вероятный язык программирования, только если предоставлю ему список кандидатов.
# test_linguist_1.rb
#!/usr/bin/env ruby
require 'linguist'
s = "int main(){}"
candidates = [Linguist::Language["Python"], Linguist::Language["C"], Linguist::Language["Ruby"]]
b = Linguist::Blob.new('', s)
langs = Linguist::Classifier.call(b, candidates)
puts langs.inspect
Исполнение:
$ ./test_linguist_1.rb
[#<Linguist::Language name=C>, #<Linguist::Language name=Python>, #<Linguist::Language name=Ruby>]
Обратите внимание, что я дал ему список кандидатов. Как я могу избежать определения списка кандидатов?
Я попробовал следующее:
# test_linguist_2.rb
#!/usr/bin/env ruby
require 'linguist'
s = "int main(){}"
candidates = Linguist::Language.all
# I also tried only Popular
# candidates = Linguist.Language.popular
b = Linguist::Blob.new('', s)
langs = Linguist::Classifier.call(b, candidates)
puts langs.inspect
Исполнение:
$ ./test_linguist_2.rb
/home/marvelez/.rvm/gems/ruby-2.2.1/gems/github-linguist-4.8.9/lib/linguist/classifier.rb:131:in `token_probability': undefined method `[]' for nil:NilClass (NoMethodError)
from /home/marvelez/.rvm/gems/ruby-2.2.1/gems/github-linguist-4.8.9/lib/linguist/classifier.rb:120:in `block in tokens_probability'
from /home/marvelez/.rvm/gems/ruby-2.2.1/gems/github-linguist-4.8.9/lib/linguist/classifier.rb:119:in `each'
from /home/marvelez/.rvm/gems/ruby-2.2.1/gems/github-linguist-4.8.9/lib/linguist/classifier.rb:119:in `inject'
from /home/marvelez/.rvm/gems/ruby-2.2.1/gems/github-linguist-4.8.9/lib/linguist/classifier.rb:119:in `tokens_probability'
from /home/marvelez/.rvm/gems/ruby-2.2.1/gems/github-linguist-4.8.9/lib/linguist/classifier.rb:105:in `block in classify'
from /home/marvelez/.rvm/gems/ruby-2.2.1/gems/github-linguist-4.8.9/lib/linguist/classifier.rb:104:in `each'
from /home/marvelez/.rvm/gems/ruby-2.2.1/gems/github-linguist-4.8.9/lib/linguist/classifier.rb:104:in `classify'
from /home/marvelez/.rvm/gems/ruby-2.2.1/gems/github-linguist-4.8.9/lib/linguist/classifier.rb:78:in `classify'
from /home/marvelez/.rvm/gems/ruby-2.2.1/gems/github-linguist-4.8.9/lib/linguist/classifier.rb:20:in `call'
from ./test_linguist.rb:21:in `block in <main>'
from ./test_linguist.rb:14:in `each'
from ./test_linguist.rb:14:in `<main>'
Дополнительно:
- Это лучший способ использовать Github Linguist? FileBlob является альтернативой Blob, но для этого необходимо записать мою строку в файл. Это проблематично по двум причинам: 1) оно медленное и 2) выбранное расширение файла ведет лингвиста, и мы не знаем правильное расширение файла.
- Есть ли лучшие инструменты для этого? Github Linguist, возможно, хорошо работает над файлами, но не над строками.
2 ответа
Бегло взглянув на исходный код Linguist, он, похоже, использует ряд стратегий для определения языка и вызывает каждую стратегию по очереди. Classifier
является последней стратегией, которая будет названа, и к этому времени (надеюсь) она выбрала языковых "кандидатов" (как вы обнаружили для себя) из предыдущих стратегий. Поэтому я думаю, что для конкретного примера, которым вы поделились с нами, вы должны передать какое-либо имя файла, даже если файл на самом деле не существует, или список кандидатов на язык. Если ни один из вариантов не подходит для вас, это может быть нереальным решением вашей проблемы.
$ ruby -r linguist -e 'p Linguist::Blob.new("foo.c", "int main(){}").language'
#<Linguist::Language name=C>
Возвращается nil
без имени файла и #<Linguist::Language name=C++>
с "foo.cc" и тем же примером кода.
Хорошей новостью является то, что вы выбрали действительно плохой образец для тестирования.:-) Другие стратегии смотрят на модели и шебанги, поэтому более сложные образцы имеют больше шансов на успех. Взгляните на это:
$ ruby -r linguist -e 'p Linguist::Blob.new("", "#!/usr/bin/env perl
print q{Hello, world!};
").language'
#<Linguist::Language name=Perl>
$ ruby -r linguist -e 'p Linguist::Blob.new("", "# vim: ft=ruby
puts %q{Hello, world!}
").language'
#<Linguist::Language name=Ruby>
Однако, если нет шебанга или моделин, нам все еще не повезло. Оказывается, есть обучающий набор данных, который вычисляется и сериализуется на диск во время установки и автоматически загружается во время определения языка. К сожалению, я думаю, что в библиотеке есть ошибка, которая не позволяет использовать этот обучающий набор данных, если на момент этого шага не было ни одного кандидата. Исправление ошибки позволяет мне сделать это:
$ ruby -Ilib -r linguist -e 'p Linguist::Blob.new("", "int main(){}").language'
#<Linguist::Language name=XC>
(Я не знаю, что такое XC, но добавляю в строку некоторые другие токены, такие как #include <stdio.h>
или же int argc, char* argv[]
дает C. Я уверен, что большинство ваших образцов будет иметь больше мяса для анализа.)
Это действительно простое исправление, и я представил PR для него. Вы можете использовать мою развилку самоцвета, если хотите. В противном случае, нам нужно изучить использование Linguist::Classify напрямую, как вы начали изучать, но это может привести к путанице.
Чтобы использовать мой форк, добавьте / измените ваш Gemfile, чтобы он читался так:
gem 'github-linguist',
require: 'linguist',
git: 'https://github.com/mwpastore/linguist.git',
branch: 'fix-no-candidates'
Я постараюсь вернуться и обновить этот ответ, когда PR был объединен, и новая версия Gem была выпущена с исправлением. Если мне придется предпринять какие-либо принудительные действия, чтобы соответствовать рекомендациям хранилища и / или осчастливить сопровождающих, вам, возможно, придется сделать bundler update
чтобы отразить изменения. Дайте знать, если у вас появятся вопросы.
Взглянув еще раз на источник Linguist, Linguist::Language.all
кажется то, что вы ищете.
РЕДАКТИРОВАТЬ: попробовал Linguist::Language.all
себя. Ошибка связана с еще одной ошибкой: некоторые языки содержат неверные данные. Например, это также не удается:
candidates = [Linguist::Language['ADA']]
Это, видимо, из-за того, что в lib/linguist/samples.json
, tokens.ADA
не существует Это не единственный такой язык.
Чтобы избежать ошибки, вы можете отфильтровать языки:
non_buggy_languages = Linguist::Samples.cache['tokens'].keys
candidates = non_buggy_languages.map { |l| Linguist::Language[l] }