在格式化的文本文件中查找文本块

Posted

技术标签:

【中文标题】在格式化的文本文件中查找文本块【英文标题】:Find a block of text in formatted text file 【发布时间】:2014-08-08 13:34:04 【问题描述】:

我经常使用 Python 解析格式化的文本文件(用于生物学研究,但我会尝试以您不需要生物学背景的方式提出我的问题。)我处理一种文件类型 - 称为 pdb 文件 -在格式化文本中包含蛋白质的 3D 结构。这是一个例子:

HEADER    CHROMOSOMAL PROTEIN                     02-JAN-87   1UBQ              
TITLE     STRUCTURE OF UBIQUITIN REFINED AT 1.8 ANGSTROMS RESOLUTION
REMARK   1                                                                      
REMARK   1 REFERENCE 1                                                          
REMARK   1  AUTH   S.VIJAY-KUMAR,C.E.BUGG,K.D.WILKINSON,R.D.VIERSTRA,           
REMARK   1  AUTH 2 P.M.HATFIELD,W.J.COOK                                        
REMARK   1  TITL   COMPARISON OF THE THREE-DIMENSIONAL STRUCTURES OF HUMAN,     
REMARK   1  TITL 2 YEAST, AND OAT UBIQUITIN                                     
REMARK   1  REF    J.BIOL.CHEM.                  V. 262  6396 1987              
REMARK   1  REFN                   ISSN 0021-9258

ATOM      1  N   MET A   1      27.340  24.430   2.614  1.00  9.67           N  
ATOM      2  CA  MET A   1      26.266  25.413   2.842  1.00 10.38           C  
ATOM      3  C   MET A   1      26.913  26.639   3.531  1.00  9.62           C  
ATOM      4  O   MET A   1      27.886  26.463   4.263  1.00  9.62           O  
ATOM      5  CB  MET A   1      25.112  24.880   3.649  1.00 13.77           C  
ATOM      6  CG  MET A   1      25.353  24.860   5.134  1.00 16.29           C  
ATOM      7  SD  MET A   1      23.930  23.959   5.904  1.00 17.17           S  
ATOM      8  CE  MET A   1      24.447  23.984   7.620  1.00 16.11           C  
ATOM      9  N   GLN A   2      26.335  27.770   3.258  1.00  9.27           N  
ATOM     10  CA  GLN A   2      26.850  29.021   3.898  1.00  9.07           C  
ATOM     11  C   GLN A   2      26.100  29.253   5.202  1.00  8.72           C  
ATOM     12  O   GLN A   2      24.865  29.024   5.330  1.00  8.22           O  
ATOM     13  CB  GLN A   2      26.733  30.148   2.905  1.00 14.46           C  
ATOM     14  CG  GLN A   2      26.882  31.546   3.409  1.00 17.01           C  
ATOM     15  CD  GLN A   2      26.786  32.562   2.270  1.00 20.10           C  
ATOM     16  OE1 GLN A   2      27.783  33.160   1.870  1.00 21.89           O  
ATOM     17  NE2 GLN A   2      25.562  32.733   1.806  1.00 19.49           N  
ATOM     18  N   ILE A   3      26.849  29.656   6.217  1.00  5.87           N  
ATOM     19  CA  ILE A   3      26.235  30.058   7.497  1.00  5.07           C  
ATOM     20  C   ILE A   3      26.882  31.428   7.862  1.00  4.01           C  
ATOM     21  O   ILE A   3      27.906  31.711   7.264  1.00  4.61           O  
ATOM     22  CB  ILE A   3      26.344  29.050   8.645  1.00  6.55           C  
ATOM     23  CG1 ILE A   3      27.810  28.748   8.999  1.00  4.72           C  
ATOM     24  CG2 ILE A   3      25.491  27.771   8.287  1.00  5.58           C  
ATOM     25  CD1 ILE A   3      27.967  28.087  10.417  1.00 10.83           C
TER      26      ILE A   3

HETATM  604  O   HOH A  77      45.747  30.081  19.708  1.00 12.43           O  
HETATM  605  O   HOH A  78      19.168  31.868  17.050  1.00 12.65           O  
HETATM  606  O   HOH A  79      32.010  38.387  19.636  1.00 12.83           O  
HETATM  607  O   HOH A  80      42.084  27.361  21.953  1.00 22.27           O   
END

ATOM 标记包含原子坐标的行的开头。 TER 标记坐标结束。我想获取包含原子坐标的整个文本块,所以我使用:

import re

f = open('example.pdb', 'r+')
content = f.read()

coor = re.search('ATOM.*TER', content) #take everthing between ATOM and TER

但它什么都不匹配。必须有一种使用正则表达式来获取整个文本块的方法。我也不明白为什么这个正则表达式模式不起作用。感谢您的帮助。

【问题讨论】:

【参考方案1】:

这应该匹配(但我还没有实际测试过):

coor = re.search('ATOM.*TER', content, re.DOTALL)

如果您阅读了documentation on DOTALL,您就会明白为什么它不起作用。

上面写的更好的方法是

coor = re.search(r'^ATOM.*^TER', content, re.MULTILINE | re.DOTALL)

要求ATOMTER 出现在换行符之后,以及使用raw string notation 的位置,这是正则表达式的惯例(尽管在这种情况下不会有任何区别)。

您也可以完全避免使用正则表达式:

start = content.index('\nATOM')
end = content.index('\nTER', start)
coor = content[start:end]

(这实际上不会在结果中包含TER,这可能会更好)。

【讨论】:

感谢您的回复。我绝对应该检查 re.DOTALL。这完全符合我最初的意图。没有正则表达式的选项也不能正常工作,因为文件中可能有多个 ATOM ... TER 块,据我所知,它只提供到第一个 TER。【参考方案2】:

你需要(?s)修饰符:

import re

f = open('example.pdb', 'w+')
content = f.read()

coor = re.search('(?s)ATOM.*TER', content)
print coor;

这将匹配所有内容 - 包括换行符 - 与 .*

请注意,如果您只需要介于两者之间的任何内容(ATOM 包括在内,TER 不包括在内),只需更改为 TER 的积极前瞻:

'(?s)ATOM.*(?=TER)'

【讨论】:

【参考方案3】:
     import re
     pattern=re.compile(r"ATOM(.*?)TER")
     print pattern.findall(string)

应该这样做。

【讨论】:

没有分组。而且 findall 效果更好,因为它提供了所有实例。【参考方案4】:

我不会使用正则表达式,而是使用 itertool 的 dropwhiletakewhile,这比将整个文件加载到内存中执行正则表达式操作更有效。 (例如,我们只是忽略文件的开头直到 ATOM,然后我们不需要在遇到 TER 后进一步从文件中读取)。

from itertools import dropwhile, takewhile

with open('example.pdb') as fin:
    until_atom = dropwhile(lambda L: not L.startswith('ATOM'), fin)
    atoms = takewhile(lambda L: L.startswith('ATOM'), until_atom)
    for atom in atoms:
        print atom,

所以这会忽略不以 ATOM 开头的行,然后在它们仍以 ATOM 开头时继续从该点获取行。如果需要,您可以将该条件更改为 lambda L: not L.startswith('TER')

您可以使用以下命令代替打印:

all_atom_text = ''.join(atoms)

获取一个大文本块。

【讨论】:

好点。当我处理大量文件时,我需要比较速度。谢谢。【参考方案5】:

非正则表达式替代方案怎么样。它可以通过一个相对简单的循环和一点点状态来实现。示例:

# Gather all sets of ATOM-TER in all_coors (if there are multiple per file).
all_coors = []

f = open('example.pdb', 'w+')
coor = None
in_atom = False
for line in f:
    if not in_atom and line.startswith('ATOM'):
        # Found first ATOM, start collecting results.
        in_atom = True
        coor = []

    elif in_atom and line.startswith('TER'):
        # Found TER, stop collecting results.
        in_atom = False
        # Save collected results.
        all_coors.append(''.join(coor))
        coor = None

    if in_atom:
        # Collect ATOM result.
        coor.append(line)

【讨论】:

我使用了非常相似的东西,逐行读取,如果行以 ATOM 开头,直到它以 TER 开头。我认为正则表达式的代码看起来更干净。但我也认为它会比 for 循环更快。 (我没有检查)

以上是关于在格式化的文本文件中查找文本块的主要内容,如果未能解决你的问题,请参考以下文章

如何读取文本文件中的文本块

如何让Hadoop读取以gz结尾的文本格式的文件

在文本文件中查找具有特定值的所有行并将它们显示在 datagridview

文本处理工具

如何设定PLSQL DEVELOPER导出的CSV文件中单元格格式为文本?

在文本块中查找单词/短语中字符的百分比