У меня есть строка:
s = ".,-2gg,,,-2gg,-2gg,,,-2gg,,,,,,,,t,-2gg,,,,,,-2gg,t,,-1gtt,,,,,,,,,-1gt,-3ggg"
и регулярное выражение, которое я использую
import re
delre = re.compile('-[0-9]+[ACGTNacgtn]+') #this is almost correct
print (delre.findall(s))
Это возвращает:
['-2gg', '-2gg', '-2gg', '-2gg', '-2gg', '-2gg', '-1gtt', '-1gt', '-3ggg']
Но -1gtt
и -1gt
не являются желаемыми совпадениями. Целое число в этом случае определяет, сколько последующих символов должно соответствовать, поэтому желаемый результат для этих двух совпадений будет -1g
и -1g
соответственно.
Есть ли способ получить целое число после тире и динамически определить регулярное выражение так, чтобы оно соответствовало такому количеству и только такому количеству последующих символов?
Существует ли предел размера этого целого числа для допустимых совпадений?
@ScottHunter почти во всех случаях можно предположить, что целое число <50
Таким образом, вы можете создать шаблон для каждого конкретного целого числа и ИЛИ их вместе. Возможно, это немного непрактично за ~50, но это ваше дело. Предложение @jonrsharpe, вероятно, подойдет.
Вы не можете сделать это напрямую с шаблоном регулярного выражения, но вы можете использовать группы захвата, чтобы разделить целочисленную и символьную части совпадения, а затем обрезать символьную часть до соответствующей длины.
import re
# surround [0-9]+ and [ACGTNacgtn]+ in parentheses to create two capture groups
delre = re.compile('-([0-9]+)([ACGTNacgtn]+)')
s = ".,-2gg,,,-2gg,-2gg,,,-2gg,,,,,,,,t,-2gg,,,,,,-2gg,t,,-1gtt,,,,,,,,,-1gt,-3ggg"
# each match should be a tuple of (number, letter(s)), e.g. ('1', 'gtt') or ('2', 'gg')
for number, bases in delre.findall(s):
# print the number, then use slicing to truncate the string portion
print(f'-{number}{bases[:int(number)]}')
Это печатает
-2gg
-2gg
-2gg
-2gg
-2gg
-2gg
-1g
-1g
-3ggg
Скорее всего, вы захотите сделать что-то кроме print
, но вы можете форматировать совпадающие строки так, как вам нужно!
ПРИМЕЧАНИЕ. Это не работает в тех случаях, когда за целым числом следует меньше совпадающих символов, чем указано, например. -10agcta
по-прежнему соответствует, хотя содержит всего 5 символов.
приятно, спасибо! можно с уверенностью предположить, что целое число и последующее количество оснований всегда совпадают. Строка в данном случае выводится командой samtools
mpileup
, поэтому, вероятно, она была тщательно протестирована.
@Райан Рад, что смог помочь! Если вы уверены, что поступающие данные всегда будут соответствовать друг другу, то это должно быть полностью работоспособно. Я не знаком с samtools
или mpileup
, но эти буквы кричали мне о ДНК, поэтому я предположил.
Еще одно альтернативное решение с использованием re.sub
, которое делает это без цикла:
import re
# surround [0-9]+ and [ACGTNacgtn]+ in parentheses to create two capture groups
delre = re.compile('[^-]*-([0-9]+)([ACGTNacgtn]+)[^-]*')
s = ".,-2gg,,,-2gg,-2gg,,,-2gg,,,,,,,,t,-2gg,,,,,,-2gg,t,,-1gtt,,,,,,,,,-1gt,-3ggg"
print (re.sub(delre, lambda m: f"-{m.group(1)}{m.group(2)[:int(m.group(1))]}\n", s))
Выход:
-2gg
-2gg
-2gg
-2gg
-2gg
-2gg
-1g
-1g
-3ggg
или же, если вы хотите вывести результат в массиве, используйте:
arr = re.sub(delre, lambda m: f"-{m.group(1)}{m.group(2)[:int(m.group(1))]} ", s).strip().split()
print (arr)
['-2gg', '-2gg', '-2gg', '-2gg', '-2gg', '-2gg', '-1g', '-1g', '-3ggg']
.strip()
не нужен.
В самом регулярном выражении нет. Но соответствующим образом обработать полученный список строк должно быть довольно просто.