基于python中的模板文件从文件中解析信息

lot*_*boy 5 python regex

我有一堆文本文件,格式都一样(这是下面的一个片段,真实文件更长):

Molecular weight = 43057.32         Residues = 391   
Average Residue Weight  = 110.121   Charge   = -10.0
Isoelectric Point = 4.8926

Residue     Number      Mole%       DayhoffStat
A = Ala     24      6.138       0.714   
B = Asx     0       0.000       0.000   
C = Cys     9       2.302       0.794   

Property    Residues        Number      Mole%
Tiny        (A+C+G+S+T)     135     34.527
Small       (A+B+C+D+G+N+P+S+T+V)   222     56.777
Aliphatic   (A+I+L+V)       97      24.808
Run Code Online (Sandbox Code Playgroud)

我必须提取所有这些变量并处理它们。我打算编写一些代码,一次遍历每一行,并通过一系列 split、strip 等函数提取相关信息。

这是人们用 python 做的一项非常常见的任务,所以我开始认为必须有一个更简单的方法。

是否有任何模块或方法可以允许以下内容:

template = """
Molecular weight = {0}          Residues = {1}   
Average Residue Weight  = {2}   Charge   = {3}
Isoelectric Point = {4}

Residue     Number      Mole%       DayhoffStat
A = Ala     {4}     {5}         {6}
B = Asx     {7}     {8}         {9}     
C = Cys     {10}    {11}        {12}    

Property    Residues        Number      Mole%
Tiny        (A+C+G+S+T)     {14}        {15}
Small       (A+B+C+D+G+N+P+S+T+V)   {16}        {17}
Aliphatic   (A+I+L+V)       {18}        {19}"""
Run Code Online (Sandbox Code Playgroud)

然后,要按照上述格式从另一个输入文件中提取变量,您将执行以下操作:

list_of_vars = Parse(template, infile)
Run Code Online (Sandbox Code Playgroud)

请注意,虽然相同的变量将出现在每个文件的同一行中,但它们可以向右移动几个字符,具体取决于同一行中它前面的值有多大。

这些文件是 emboss pepstats 的输出,以防有人想知道。

解决方案

感谢大家的快速回复。这里的解决方案是在 re 模块中使用 findall 函数。下面是一个简单的例子:

import re

class TemplateParser:
    def __init__(self, template):
        self.m_template = template.replace('{}', r'[\s]*([\d\-\.]+)[\s]*')

    def ParseString(self, filename):
        return re.findall(self.m_template, filename, re.DOTALL|re.MULTILINE)[0]

template = """
Molecular weight = {}          Residues = {}   
Average Residue Weight  = {}   Charge   = {}
Isoelectric Point = {}

Residue     Number      Mole%       DayhoffStat
A = Ala     {}    {}        {}
B = Asx     {}    {}        {}     
C = Cys     {}    {}        {}    

Property    Residues        Number      Mole%
Tiny        \(A\+C\+G\+S\+T\)     {}        {}
Small       \(A\+B\+C\+D\+G\+N\+P\+S\+T\+V\)   {}        {}
Aliphatic   \(A\+I\+L\+V\)       {}        {}"""
Run Code Online (Sandbox Code Playgroud)

ParseString 函数成功返回一个字符串列表,然后我可以处理它。由于文件的格式始终相同,因此我能够成功处理所有文件。我只有两个问题。

1) 正如你在上面看到的,我已经在我的模板文件中转义了所有的正则表达式字符,这没什么大不了的。

2) 正如我上面也提到的,这个模板只是我需要解析的实际文件的一小部分。当我用我的真实数据尝试这个时,重新抛出以下错误:

"sorry, but this version only supports 100 named groups" AssertionError: sorry, but this version only supports 100 named groups
Run Code Online (Sandbox Code Playgroud)

我通过将模板字符串拆分为 3 个部分来解决此问题,使用 3 个不同的模板运行 ParseString 函数 3 次,并将列表结果添加到一起。

再次感谢!

kdo*_*pen 2

这是一个艰难的开始

In [3]: data = """Molecular weight = 43057.32         Residues = 391   
   ...: Average Residue Weight  = 110.121   Charge   = -10.0
   ...: Isoelectric Point = 4.8926
   ...: 
   ...: Residue     Number      Mole%       DayhoffStat
   ...: A = Ala     24      6.138       0.714   
   ...: B = Asx     0       0.000       0.000   
   ...: C = Cys     9       2.302       0.794   
   ...: 
   ...: Property    Residues        Number      Mole%
   ...: Tiny        (A+C+G+S+T)     135     34.527
   ...: Small       (A+B+C+D+G+N+P+S+T+V)   222     56.777
   ...: Aliphatic   (A+I+L+V)       97      24.808
   ...: """
In [5]: rx=r'Molecular weight += +([0-9\.]+).*Residues += +([0-9]+).*Average Residue Weight += +([0-9\.]+).*Charge += +([-+]*[0-9\.]+)'
     rx=r'Molecular weight += +([0-9\.]+).*Residues += +([0-9]+).*Average Residue Weight += +([0-9\.]+).*Charge += +([-+]*[0-9\.]+)'
In [7]: import re
In [12]: re.findall(rx, data, re.DOTALL|re.MULTILINE)
Out[12]: [('43057.32', '391', '110.121', '-10.0')]
Run Code Online (Sandbox Code Playgroud)

如您所见,这会从文件中提取前 4 个字段。如果您确实有这样的固定格式文件,则可以扩展正则表达式以在一次调用中获取所有数据。

您需要完善子表达式以获得正确的浮点格式等 - 正如我所说,这是一个快速的概念验证。如果实际文件明显更大,那么 RE 可能会变得非常长或难以调试。

只是为了进行比较,以下是使用 ctwheels 在其评论中提供的正则表达式获得的相同数据

In [13]: rx2='(?:\s*([a-zA-Z()+ ]+?)[ =]*)([-+]?\d+\.?\d*)'

In [14]: re.findall(rx2,data)
Out[14]: 
[('Molecular weight', '43057.32'),
 ('Residues', '391'),
 ('Average Residue Weight', '110.121'),
 ('Charge', '-10.0'),
 ('Isoelectric Point', '4.8926'),
 ('Ala', '24'),
 (' ', '6.138'),
 (' ', '0.714'),
 ('Asx', '0'),
 (' ', '0.000'),
 (' ', '0.000'),
 ('Cys', '9'),
 (' ', '2.302'),
 (' ', '0.794'),
 ('Tiny        (A+C+G+S+T)', '135'),
 (' ', '34.527'),
 ('Small       (A+B+C+D+G+N+P+S+T+V)', '222'),
 (' ', '56.777'),
 ('Aliphatic   (A+I+L+V)', '97'),
 (' ', '24.808')]
In [15]: [m[1] for m in _]
Out[15]: 
['43057.32',
 '391',
 '110.121',
 '-10.0',
 '4.8926',
 '24',
 '6.138',
 '0.714',
 '0',
 '0.000',
 '0.000',
 '9',
 '2.302',
 '0.794',
 '135',
 '34.527',
 '222',
 '56.777',
 '97',
 '24.808']
Run Code Online (Sandbox Code Playgroud)

这可能已经足够好了