解析化学式

时间:2010-06-04 13:05:28

标签: java chemistry

我正在尝试为应用程序编写一个方法,该方法采用类似“CH3COOH”的化学式,并返回一些充满符号的集合。

CH3COOH会返回[C,H,H,H,C,O,O,H]

我已经有了一些有点工作的东西,但它非常复杂并且使用了大量嵌套if-else结构和循环的代码。

有没有办法可以通过使用String.split的某种正则表达式或者其他一些简单的代码来实现这一点?

5 个答案:

答案 0 :(得分:31)

我已经开发了几篇关于如何解析分子式的系列文章,包括更复杂的公式如C6H2(NO2)3CH3。

最近的一篇是我在PyCon2010上的演讲“PLY and PyParsing”,我在那里使用分子公式评估器比较这两个Python解析系统作为我的样本问题。甚至有video of my presentation

该演示文稿基于three-part series of articles我使用ANTLR开发分子式解析器。在part 3中,我将ANTLR解决方案与PLY和PyParsing中的手写正则表达式解析器和解决方案进行了比较。

regexp和PLY解决方案首先在two-part series中开发,用两种方式编写Python解析器。

正则表达式解决方案和基本ANTLR / PLY / PyParsing解决方案使用类似[A-Z] [a-z]?\ d *的正则表达式来匹配公式中的术语。这就是@David M所建议的。

这是用Python编写的

import re

# element_name is: capital letter followed by optional lower-case
# count is: empty string (so the count is 1), or a set of digits
element_pat = re.compile("([A-Z][a-z]?)(\d*)")

all_elements = []
for (element_name, count) in element_pat.findall("CH3COOH"):
    if count == "":
        count = 1
    else:
        count = int(count)
    all_elements.extend([element_name] * count)

print all_elements

当我运行它(它的硬编码使用醋酸,CH3COOH)时,我得到了

['C', 'H', 'H', 'H', 'C', 'O', 'O', 'H']

请注意,这一小段代码假设分子式正确。如果你给它类似“## $%^ O2#$$#”那么它会忽略它不知道的字段并给出['O','O']。如果你不想要那么你就必须让它更健壮。

如果你想支持更复杂的公式,比如C6H2(NO2)3CH3,那么你需要了解一下树数据结构,特别是(如@Roman指出的),抽象语法树(通常称为AST) )。这太复杂了,不能进入这里,所以请参阅我的演讲和论文以获取更多细节。

答案 1 :(得分:24)

假设它正确大写,则等式中的每个符号都匹配此正则表达式:

[A-Z][a-z]*\d*

(对于化学挑战,元素的符号始终为大写字母,后跟可选的小写1或可能为2 - 例如汞的汞)

您可以捕获元素符号和组中的数字,如下所示:

([A-Z][a-z]*)(\d*)

所以是的,从理论上讲,这将是正则表达式可以帮助的东西。如果你正在处理像C 6 H 2 (NO 2 3 (CH 的公式3 3 然后你的工作当然有点难......

答案 2 :(得分:12)

如果只需要处理简单的案例,那么使用正则表达式的解决方案是最好的方法。否则,您需要构建类似Abstract Syntax Tree的内容并对其进行评估或使用Polish Notation

例如,TNT公式C6H2(NO2)3CH3应该表示为:

(+ (* C 6) (* H 2) (* (+ N (* O 2)) 3) C (+ H 3))

答案 3 :(得分:4)

您是否考虑过在Chemical Markup Language中表达化学式?它非常通用,有很多工具/观察器可以将这些化学论坛或化合物呈现为2D到3D。

答案 4 :(得分:2)

我正在研究一个需要摩尔质量计算化学公式的程序,因此我创建了一个适用于各种公式的解决方案。

例如,“(CH3)16(Tc(H2O)3CO(BrFe3(ReCl)3(SO4)2)2)2MnO4”将产生“16C 48H 2Tc 12H 6O 2C 2O 4Br 12Fe 12Re 12Cl 8S 32O Mn 4O“(这种化合物是组成的,但是嘿,它有效!)

此代码是用C#编写的,这就是我没有发布它的原因。如果您有兴趣,我可以为您发布。在注意到java标记之前,我实际上写了一个完整的答案。

无论如何,它的工作原理是基本上对递归括号括起来的原子块进行分组。它不处理诸如2Pb(但(Pb)2或Pb2确实起作用)或带电化合物如OH-的系数。

绝不是简单或优雅。我确实想要一个有效的解决方案,所以我知道有更好的方法(我从未尝试过正则表达式!)。但它适用于我需要的公式,也许它也适合你的公式。

以下是我运行它的一些测试用例。看看它们,让我知道C#代码是否仍然对您有用。格式为(输入,预期输出)

        ("Pb ", " Pb"); 
        ("H ", " H"); 
        ("Pb2 ", " 2Pb"); 
        ("H2 ", " 2H");             
        ("3Pb2 ", " 6Pb");
        ("Pb2SO4", " 2Pb S 4O");                                     
        ("PbH2 ", " Pb 2H");            
        ("(PbH2)2 ", " 2Pb 4H");
        ("(CCC)2 ", " 2C 2C 2C");
        ("Pb(H2)2 ", " Pb 4H");            
        ("(Pb(H2)2)2 ", " 2Pb 8H"); 
        ("(Pb(H2)2)2NO3 ", " 2Pb 8H N 3O"); 
        ("(Ag(Pb(H2)2)2)2SO4 ", " 2Ag 4Pb 16H S 4O");             
        ("Pb(CH3(CH2)2CH3)2", " Pb 2C 6H 4C 8H 2C 6H"); 
        ("Na2(CH3(CH2)2CH3)2", " 2Na 2C 6H 4C 8H 2C 6H");
        ("Tc(H2O)3Fe3(SO4)2", " Tc 6H 3O 3Fe 2S 8O");
        ("Tc(H2O)3(Fe3(SO4)2)2", " Tc 6H 3O 6Fe 4S 16O");
        ("(Tc(H2O)3(Fe3(SO4)2)2)2", " 2Tc 12H 6O 12Fe 8S 32O");
        ("(Tc(H2O)3CO(Fe3(SO4)2)2)2", " 2Tc 12H 6O 2C 2O 12Fe 8S 32O");
        ("(Tc(H2O)3CO(BrFe3(ReCl)3(SO4)2)2)2MnO4", " 2Tc 12H 6O 2C 2O 4Br 12Fe 12Re 12Cl 8S 32O Mn 4O");
        ("(CH3)16(Tc(H2O)3CO(BrFe3(ReCl)3(SO4)2)2)2MnO4", " 16C 48H 2Tc 12H 6O 2C 2O 4Br 12Fe 12Re 12Cl 8S 32O Mn 4O");