【问题标题】: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

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

【问题讨论】:

    标签: python regex text-formatting


    【解决方案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 循环更快。 (我没有检查)
      猜你喜欢
      • 2014-03-15
      • 1970-01-01
      • 2021-06-23
      • 1970-01-01
      • 2015-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多