提取2个特定标签之间的线

时间:2018-12-20 10:59:03

标签: python regex

对于一个常规的编程问题,我需要提取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*

Ending_delimiter到处都是一样的。

起始定界符,即定界符1,直到n的定界符2均来自列表。

捕获的是,在文件中,每个起始定界符后面都有几个(少于3个)字符,这些字符与起始定界符结合在一起,用作文本行的标识符,直到ending_delimiter,这是一种“ 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 - Read file from and to specific lines of text

但没有成功。

我当前正在尝试的实现是上面链接的答案之一。

感谢您的帮助。

P.S:在Windows 10上的PyCharm上使用Python 2.7。

5 个答案:

答案 0 :(得分:1)

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)

答案 1 :(得分:1)

我建议通过以下方式修复您的代码:

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 demo

您当前的代码存在很多问题:

  • block = elemblock设为字节字符串,而另外的.append导致了异常
  • 您仅捕获了一次出现的块,因为对它进行细化后,您有一条break语句
  • 所有行都添加为单独的项目,而您需要将它们收集到列表中,然后将其与\n合并以将字符串粘贴到结果列表中
  • 您不需要使用正则表达式来检查字符串是否出现在字符串的开头,请使用str.startswith方法。

答案 2 :(得分:1)

到我发现这个问题时,已经有了很多好的答复,但是我的方法是,您可以使用以下方法解决此问题:

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 module

答案 3 :(得分:0)

我将通过以下方式做到这一点:例如,让<d1><d2><d3>成为我们的开始定界符,<d>结束定界符和string是您正在处理的文本。然后执行以下代码:

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

将给出元组列表,每个元组包含开始定界符,主体和结束定界符。此代码使用正则表达式(方括号)内的分组,正则表达式中的竖线(|)的行为类似于或,点(。)与DOTALL标志结合使用可匹配任何字符,加号(+)表示1个或多个,问题(?)非-贪婪的方式(在这种情况下这很重要,否则您将获得从第一个开始的定界符开始到最后一个结束定界符结束的单个匹配)

答案 4 :(得分:0)

我的re-less解决方案如下:

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'}