Существуют инструменты, предназначенные специально для работы с данными формата BED, которые вы бы хорошо изучили. bedtools , вероятно, является наиболее распространенным и простым в получении, и у него есть доступная оболочка для python , если вам абсолютно необходимо использовать python.
Инструмент multiIntersectBed - это то, что вам, вероятно, нужно, и его следует использовать довольно просто:
Пример использования:
== Input files: ==
$ cat a.bed
chr1 6 12
chr1 10 20
chr1 22 27
chr1 24 30
$ cat b.bed
chr1 12 32
chr1 14 30
$ cat c.bed
chr1 8 15
chr1 10 14
chr1 32 34
$ cat sizes.txt
chr1 5000
== Multi-intersect the files: ==
$ multiIntersectBed -i a.bed b.bed c.bed
chr1 6 8 1 1 1 0 0
chr1 8 12 2 1,3 1 0 1
chr1 12 15 3 1,2,3 1 1 1
chr1 15 20 2 1,2 1 1 0
chr1 20 22 1 2 0 1 0
chr1 22 30 2 1,2 1 1 0
chr1 30 32 1 2 0 1 0
chr1 32 34 1 3 0 0 1
== Multi-intersect the files, with a header line (titles are the file names): ==
$ multiIntersectBed -header -i a.bed b.bed c.bed
chrom start end num list a.bed b.bed c.bed
chr1 6 8 1 1 1 0 0
chr1 8 12 2 1,3 1 0 1
chr1 12 15 3 1,2,3 1 1 1
chr1 15 20 2 1,2 1 1 0
chr1 20 22 1 2 0 1 0
chr1 22 30 2 1,2 1 1 0
chr1 30 32 1 2 0 1 0
chr1 32 34 1 3 0 0 1
== Multi-intersect the files, with a header line and custom names: ==
$ multiIntersectBed -header -i a.bed b.bed c.bed -names A B C
chrom start end num list A B C
chr1 6 8 1 A 1 0 0
chr1 8 12 2 A,C 1 0 1
chr1 12 15 3 A,B,C 1 1 1
chr1 15 20 2 A,B 1 1 0
chr1 20 22 1 B 0 1 0
chr1 22 30 2 A,B 1 1 0
chr1 30 32 1 B 0 1 0
chr1 32 34 1 C 0 0 1
== Multi-intersect the files, showing empty regions (note, requires -g): ==
$ multiIntersectBed -header -i a.bed b.bed c.bed -names A B C -empty -g sizes.txt
chrom start end num list A B C
chr1 0 6 0 none 0 0 0
chr1 6 8 1 A 1 0 0
chr1 8 12 2 A,C 1 0 1
chr1 12 15 3 A,B,C 1 1 1
chr1 15 20 2 A,B 1 1 0
chr1 20 22 1 B 0 1 0
chr1 22 30 2 A,B 1 1 0
chr1 30 32 1 B 0 1 0
chr1 32 34 1 C 0 0 1
chr1 34 5000 0 none 0 0 0
Затем можно выполнить фильтрацию по 4-му столбцу, чтобы удовлетворить ваши оговоренные состояние для регионов.
Для реального языка лексический анализатор сказал способ пойти - как Guss. Но если полный язык является только столь же сложным как Ваш пример, можно использовать этот быстрый взлом:
irb> text = %{Children^10 Health "sanitation management"^5}
irb> text.scan(/(?:(\w+)|"((?:\\.|[^\\"])*)")(?:\^(\d+))?/).map do |word,phrase,boost|
{ :keywords => (word || phrase).downcase, :boost => (boost.nil? ? nil : boost.to_i) }
end
#=> [{:boost=>10, :keywords=>"children"}, {:boost=>nil, :keywords=>"health"}, {:boost=>5, :keywords=>"sanitation management"}]
При попытке проанализировать регулярный язык затем, то этот метод будет достаточен - хотя не потребовалось бы значительно большего количества сложностей для создания языка нерегулярным.
Быстрая разбивка regex:
\w+
соответствия любые ключевые слова единственного термина(?:\\.|[^\\"]])*
использование не получая круглые скобки ((?:...)
) соответствовать содержанию завершенной двойной заключенной в кавычки строки - любой завершенный символ (\n
, \"
, \\
, и т.д.), или любой отдельный символ это не управляющий символ escape или кавычка конца."((?:\\.|[^\\"]])*)"
получения только содержание заключенной в кавычки фразы ключевого слова.(?:(\w+)|"((?:\\.|[^\\"])*)")
соответствия любое ключевое слово - единственный термин или фраза, получая единственные условия в $1
и содержание фразы в $2
\d+
соответствует числу.\^(\d+)
получает число после каре (^
). Так как это - третий набор получения круглых скобок, это будет caputred в $3
.(?:\^(\d+))?
получает число после каре, если это там, соответствует пустой строке иначе.String#scan(regex)
соответствует regex против строки максимально много раз, вывод массив "соответствий". Если regex содержит получение parens, "соответствие" является массивом полученных объектов - так $1
становится match[0]
, $2
становится match[1]
, и т.д. Любая круглая скобка получения, которая не становится подобранной против части строки, отображается на a nil
запись в получающемся "соответствии".
#map
затем берет эти соответствия, использует некоторое волшебство блока повредить каждый полученный термин в различные переменные (мы, возможно, сделали do |match| ; word,phrase,boost = *match
), и затем создает Ваши желаемые хеши. Точно один из word
или phrase
будет nil
, так как оба не могут быть подобраны против входа, таким образом, (word || phrase)
возвратится не -nil
один, и #downcase
преобразует его в весь нижний регистр. boost.to_i
преобразует строку в целое число в то время как (boost.nil? ? nil : boost.to_i)
гарантирует это nil
повышения остаются nil
.
Вот неустойчивое использование в качестве примера StringScanner
. Это - код, который я просто адаптировал от Теста Ruby: JSON Парсинга, который имеет превосходное объяснение.
require 'strscan'
def test_parse
text = %{Children^10 Health "sanitation management"^5}
expected = [{:keywords=>"children", :boost=>10}, {:keywords=>"health", :boost=>nil}, {:keywords=>"sanitation management", :boost=>5}]
assert_equal(expected, parse(text))
end
def parse(text)
@input = StringScanner.new(text)
output = []
while keyword = parse_string || parse_quoted_string
output << {
:keywords => keyword,
:boost => parse_boost
}
trim_space
end
output
end
def parse_string
if @input.scan(/\w+/)
@input.matched.downcase
else
nil
end
end
def parse_quoted_string
if @input.scan(/"/)
str = parse_quoted_contents
@input.scan(/"/) or raise "unclosed string"
str
else
nil
end
end
def parse_quoted_contents
@input.scan(/[^\\"]+/) and @input.matched
end
def parse_boost
if @input.scan(/\^/)
boost = @input.scan(/\d+/)
raise 'missing boost value' if boost.nil?
boost.to_i
else
nil
end
end
def trim_space
@input.scan(/\s+/)
end
То, что Вы имеете, вот произвольная грамматика, и проанализировать его, что Вы действительно хотите, является лексическим анализатором - можно записать файл грамматики, который описал синтаксис, и затем используйте лексический анализатор для генерации рекурсивного синтаксического анализатора от грамматики.
Запись лексического анализатора (или даже рекурсивный синтаксический анализатор) не действительно тривиальна - хотя это - полезное упражнение в программировании - но можно найти список лексических анализаторов/синтаксических анализаторов Ruby в этом электронном письме здесь: http://newsgroups.derkeiler.com/Archive/Comp/comp.lang.ruby/2005-11/msg02233.html
RACC доступен как стандартный модуль Ruby 1.8, таким образом, я предлагаю, чтобы Вы сконцентрировались на том, что, даже если его руководству не действительно легко следовать и оно требует знакомства с yacc.