在格式化文本文件中查找文本块
我经常用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
但是它什么都没有匹配到。肯定有办法通过正则表达式提取整个文本块。我也不明白为什么这个正则表达式的模式不管用。希望能得到一些帮助。
5 个回答
我不会使用正则表达式,而是会用工具库里的 dropwhile
和 takewhile
,这样比把整个文件都加载到内存里去做正则匹配要高效得多。比如,我们可以先忽略文件开头的内容,直到遇到 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 后,就继续读取以 ATOM 开头的行。如果你想的话,可以把这个条件改成 lambda L: not L.startswith('TER')
。
如果不想打印出来,你可以使用:
all_atom_text = ''.join(atoms)
这样就能得到一大块文本。
有没有一种不使用正则表达式的替代方法呢?其实可以通过一个相对简单的循环和一点状态管理来实现。下面是一个例子:
# 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)
你需要使用 (?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)'
import re
pattern=re.compile(r"ATOM(.*?)TER")
print pattern.findall(string)
这样做就可以了。
这个应该可以匹配(不过我还没实际测试过):
coor = re.search('ATOM.*TER', content, re.DOTALL)
如果你查看一下关于 DOTALL
的文档,你就会明白为什么之前的代码没有起作用。
还有一种更好的写法是:
coor = re.search(r'^ATOM.*^TER', content, re.MULTILINE | re.DOTALL)
在这里,ATOM
和 TER
必须出现在换行符之后,并且使用了原始字符串表示法,这在正则表达式中是常见的做法(虽然在这个例子中其实没有什么区别)。
你也可以完全不使用正则表达式:
start = content.index('\nATOM')
end = content.index('\nTER', start)
coor = content[start:end]
(这样做实际上不会把 TER
包含在结果中,这可能更好)。