Извлеките строки между 2 конкретными тегами - PullRequest
0 голосов
/ 20 декабря 2018

Для обычного вопроса программирования мне нужно извлечь несколько строк текста, которые находятся между 2 тегами (разделители, если мне нужно быть более конкретными).

Файл выглядит примерно так:

*some random text*

...

...

...

tag/delimiter 1

text 1   #extract

text 2   #extract

...      #extract

...      #extract

text n   #extract

tag/ending_delimiter

*some random text*


...

...

...
tag/delimiter 2

text 1   #extract

text 2   #extract

...      #extract

...      #extract

text n   #extract

tag/ending_delimiter

*some random text*


...

...

...

tag/delimiter n

text 1   #extract

text 2   #extract

...      #extract

...      #extract

text n   #extract

tag/ending_delimiter

*some random text until the file ends*

Параметр end_delimiter везде одинаков.

Начальный разделитель, т.е. разделитель 1, разделитель 2 до n, взят из списка.

Подвох в файлепосле каждого начального разделителя есть несколько (менее 3) символов, которые в сочетании с начальным разделителем работают в качестве идентификатора для строк текста до конечного_длимитера, своего рода «uid», технически.

Пока что я попробовал следующее:

data_file = open("file_name")
block = []
found = False

for elem in list_of_starting_delimiters:
    for line in data_file:
        if found:
            block.append(line)
            if re.match(attribute_end, line.strip()):
                break
        else:
            if re.match(elem, line.strip()):
                found = True
                block = elem

data_file.close()

Я также пытался реализовать ответы, предложенные в:

python - чтение файлаот и до определенных строк текста

, но безуспешно.

Реализация, которую я сейчас пробую, является одним из ответов по ссылке выше.

Любая помощь приветствуется.

PS: Использование Python 2.7, в PyCharm, в Windows 10.

Ответы [ 5 ]

0 голосов
/ 20 декабря 2018

My re без решения будет следующим:

list_of_starting_delimiters = ['tag/delimiter 1', 'tag/delimiter 2', 'tag/delimiter n']
enddel = 'tag/ending_delimiter'

block ={}
section = ''
with open(file, 'r') as f:
    for line in f:
        if line.strip() == enddel:
            section = ''
        if section:
            block[section] = block.get(section, '') + line
        if line.strip() in list_of_starting_delimiters:
            section = line.strip()

print(block)

Извлекает блоки в словарь с тегами начального разделителя в качестве ключей и соответствующими разделами в качестве значений.
Требуется запуски конечные теги являются единственным содержимым соответствующих строк.

Вывод:

{'tag/delimiter 1':
'\ntext 1   #extract\n\ntext 2   #extract\n\n...      #extract\n\n...      #extract\n\ntext n   #extract\n\n', 
'tag/delimiter 2':
'\ntext 1   #extract\n\ntext 2   #extract\n\n...      #extract\n\n...      #extract\n\ntext n   #extract\n\n', 
'tag/delimiter n':
'\ntext 1   #extract\n\ntext 2   #extract\n\n...      #extract\n\n...      #extract\n\ntext n   #extract\n\n'}                            
0 голосов
/ 20 декабря 2018

К тому времени, как я это выяснил, уже было достаточно хороших ответов, но мой подход состоял бы в том, что вы могли бы решить эту проблему с помощью:

import re
pattern = re.compile(r"(^tag\/delimiter) (.{0,3})\n\n((^[\w\d #\.]*$\n)+)^(tag\/ending_delimiter)", re.M)

Затем вы могли бы найти все совпадения в своем тексте.выполнив:

 for i in pattern.finditer(<target_text>):
    #do something with each match

 pattern.findAll(<target_text>) - returns a list of strings of all matches

Это, конечно, требует оговорки, что вам нужно указать разные разделители и скомпилировать разные шаблоны регулярных выражений (re.compile) для каждого отдельного разделителя, используя переменные и конкатенацию строк как @SpghttCdпоказывает в своем ответе

Для получения дополнительной информации см. модуль python re

0 голосов
/ 20 декабря 2018

А как же

import re

with open(file, 'r') as f:
    txt = f.read()

losd = '|'.join(list_of_starting_delimiters)
enddel = 'attribute_end'
block = re.findall('(?:' + losd + r')([\s\S]*?)' + enddel, txt)
0 голосов
/ 20 декабря 2018

Я предлагаю исправить ваш код следующим образом:

block = []
found = False
list_of_starting_delimiters = ['tag/delimiter']
attribute_end = 'tag/ending_delimiter'
curr = []

for elem in list_of_starting_delimiters:
    for line in data_file:
        if found:
            curr.append(line)
            if line.strip().startswith(attribute_end):
                found = False
                block.append("\n".join(curr))            # Add merged list to final list
                curr = []                                # Zero out current list
        else: 
            if line.strip().startswith(elem):            # If line starts with start delimiter
                found = True
                curr.append(line.strip())                # Append line to current list 

if len(curr) > 0:      # If there are still lines in the current list
    block.append(curr) # Add them to the final list

См. Демонстрационную версию Python

С вашим текущим кодом довольно много проблем:

  • block = elem сделал block байтовой строкой, а дальнейшее .append вызвало исключение
  • Вы захватили только одно вхождение блока, потому что после штрафования вы получили break оператор
  • Все строки были добавлены как отдельные элементы, в то время как вам нужно было собрать их в список, а затем соединить их с \n, чтобы получить строки для вставки в результирующий список
  • Youне нужно регулярное выражение, чтобы проверить, появляется ли строка в начале строки, используйте метод str.startswith.
0 голосов
/ 20 декабря 2018

Я бы сделал это следующим образом: Например, пусть <d1> и <d2> и <d3> будут нашими начальными разделителями, <d> конечным разделителем и string - обрабатываемым вами текстом.Затем следующая строка кода:

re.findall('(<d1>|<d2>|<d3>)(.+?)(<d>)',string,re.DOTALL)

выдаст список кортежей, каждый из которых содержит начальный разделитель, тело и конечный разделитель.Этот код использует группировку внутри регулярного выражения (в скобках), pipe (|) в регулярных выражениях действует аналогично или, точка (.) В сочетании с флагом DOTALL соответствует любому символу, плюс (+) означает 1 или более, вопрос (?) Нежадный способ (это важно в этом случае, так как в противном случае вы получите одно совпадение, начинающееся с первого начального разделителя и заканчивающееся на последнем конечном разделителе)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...