在Python中基于模板文件解析文件信息

6

我是一个文本文件的集合,它们都采用相同的格式(以下是一小段示例,实际文件更长):

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

我需要提取所有这些变量并进行处理。我打算编写一些代码,逐行处理并通过一系列的分割、去空格等函数提取相关信息。

这是人们经常使用 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}"""

然后,要从符合上述格式的另一个输入文件中提取变量,您需要执行以下操作:
list_of_vars = Parse(template, infile)

请注意,同一行中的同一变量将在每个文件中出现,但是根据该行上前面的值有多大,它们可能会向右移动几个字符。
这些文件是从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\)       {}        {}"""

ParseString函数成功返回一个字符串列表,我可以对其进行处理。由于文件格式始终相同,我能够成功处理所有文件。但是,我遇到了两个问题。
1)如上所述,我必须转义模板文件中的所有正则表达式字符,这并不是什么大问题。
2)正如我上面提到的,这个模板只是我需要解析的实际文件的一小部分。当我尝试使用我的真实数据时,re抛出了以下错误:
"sorry, but this version only supports 100 named groups" AssertionError: sorry, but this version only supports 100 named groups

我通过将模板字符串分成三个部分来解决了这个问题,使用三个不同的模板运行ParseString函数,并将列表结果相加。
再次感谢!

我经常使用Python进行解析,但不知道有没有模板方法可以做到这一点。我总是写一堆正则表达式/分割。希望有人能给出答案 :) - sniperd
有趣的问题,但不幸的是可能不属于主题(请求我们找到工具或库等的问题)。 - kdopen
你可能可以将你的模板转换成多行正则表达式...实际上请参见 https://dev59.com/zkrSa4cB1Zd3GeqPYrnJ - kdopen
2
不确定这是否符合您的要求,但如果它对您有用,我将把它作为答案发布: (?:\s*([a-zA-Z()+ ]+?)[ =]*)([-+]?\d+\.?\d*)。在此处查看其使用情况:https://regex101.com/r/dimHON/1 - ctwheels
2个回答

3
我可以看到这个帖子很久以前就已经得到了回答,但是我和帖子的原作者有相同的想法 - 使用模板来解析文本数据 - 最终创建了 Template Text Parser 模块:https://ttp.readthedocs.io/en/latest/ 以下是用于解析原帖作者文本的 Python 代码示例:
template = """
<group>
Molecular weight = {{ Molecular_weight }}         Residues = {{ Residues }}   
Average Residue Weight  = {{ Average_Residue_Weight }}   Charge   = {{ Charge }}
Isoelectric Point = {{ Isoelectric_Point }}

<group name="table1">
## Residue              Number                Mole%       DayhoffStat
{{ Residue | PHRASE }}  {{ Number | DIGIT }}  {{ Mole }}  {{ DayhoffStat }}
</group>

<group name="table2">
## Property     Residues        Number                Mole%
{{ Property }}  {{ Residues }}  {{ Number | DIGIT }}  {{ Mole }}
</group>
</group>
"""

sample_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
"""

from ttp import ttp
parser = ttp(sample_data, template)
result = parser.result(format="pprint")
print(result[0])

将产生:

[   {   'Average_Residue_Weight': '110.121',
        'Charge': '-10.0',
        'Isoelectric_Point': '4.8926',
        'Molecular_weight': '43057.32',
        'Residues': '391',
        'table1': [   {   'DayhoffStat': '0.714',
                          'Mole': '6.138',
                          'Number': '24',
                          'Residue': 'A = Ala'},
                      {   'DayhoffStat': '0.000',
                          'Mole': '0.000',
                          'Number': '0',
                          'Residue': 'B = Asx'},
                      {   'DayhoffStat': '0.794',
                          'Mole': '2.302',
                          'Number': '9',
                          'Residue': 'C = Cys'}],
        'table2': [   {   'Mole': '34.527',
                          'Number': '135',
                          'Property': 'Tiny',
                          'Residues': '(A+C+G+S+T)'},
                      {   'Mole': '56.777',
                          'Number': '222',
                          'Property': 'Small',
                          'Residues': '(A+B+C+D+G+N+P+S+T+V)'},
                      {   'Mole': '24.808',
                          'Number': '97',
                          'Property': 'Aliphatic',
                          'Residues': '(A+I+L+V)'}]}]

太好了!感谢您的贡献!我已经在我的应用程序中下载了TTP并正在使用它! - AeroHil

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')]

您可以看到,这个方法从文件中提取了前4个字段。如果您确实有一个像这样的固定格式文件,您可以扩展正则表达式以一次获取所有数据。

您需要修改子表达式以获取正确的浮点格式等 - 如我所说,这只是一个快速概念验证。如果真实文件显著更大,则正则表达式可能变得非常冗长或难以调试。

仅供比较,以下是使用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']

这可能已经足够了


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接