解析化学式

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

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

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

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

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

[AZ][az]*\d* 

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

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

 ([AZ][az]*)(\d*) 

所以是的,从理论上讲,这将是正则表达式可以帮助的东西。 如果您正在处理像C 6 H 2 (NO 23 (CH 33这样的公式,那么您的工作当然有点难…

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

最近的一篇是我在PyCon2010上的演讲“ PLY和PyParsing ”,我将这两个Python解析系统与分子公式评估器作为我的样本问题进行比较。 甚至还有我的演示video 。

该演示文稿基于我使用ANTLR开发分子式解析器的三部分系列文章 。 在第3部分中,我将ANTLR解决方案与PLY和PyParsing中的手写正则表达式解析器和解决方案进行了比较。

regexp和PLY解决方案最初是在两部分系列中开发的,介绍了在Python中编写解析器的两种方法。

正则表达式解决方案和基本ANTLR / PLY / PyParsing解决方案使用正则表达式,如[AZ] [az]?\ 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("([AZ][az]?)(\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)。 这太复杂了,不能进入这里,所以请看我的谈话和论文了解更多细节。

如果您只需要处理简单的情况,那么使用正则表达式的解决方案是最好的方法。 否则,您需要构建类似抽象语法树的内容并对其进行评估或使用波兰表示法 。

例如,TNT式C6H2(NO2)3CH3应如下所示:

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

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

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

例如,“(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标记之前,我实际上写了一个完整的答案。

无论如何,它的工作原理是基本上将递归匹配的primefaces块分组。 它不处理诸如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");