Java 解析化学式

Java 解析化学式,java,chemistry,Java,Chemistry,我正在尝试为一个应用程序编写一个方法,该应用程序采用“CH3COOH”这样的化学公式,并返回一些充满符号的集合 CH3COOH将返回[C,H,H,C,O,O,H] 我已经有了一些可以工作的东西,但是它非常复杂,使用了大量的代码和嵌套的if-else结构和循环 有没有一种方法可以通过使用某种正则表达式和String.split或其他一些优秀的简单代码来实现这一点?假设它正确大写,等式中的每个符号都与此正则表达式匹配: [A-Z][a-z]*\d* (对于受到化学挑战的元素,元素的符号始终是大写字

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

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

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


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

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

[A-Z][a-z]*\d*
(对于受到化学挑战的元素,元素的符号始终是大写字母,后跟可选的小写字母1或可能是小写字母2,例如汞代表汞)

您可以按如下方式分组捕获元素符号和数字:

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

是的,从理论上讲,正则表达式可以帮助解决这个问题。如果你在处理C6H2(NO2)3(CH3)3这样的公式,那么你的工作当然会有点困难…

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

例如,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)2MnO4”将导致“16C 48H 2Tc 12H 6O 2C 2O 4Br 12Fe 12Re 12Cl 8S 32O Mn 4O”(这种化合物是合成的,但它是有效的!)

这段代码是用C#编写的,所以我没有发布它。如果你感兴趣,我可以帮你贴。在注意到java标记之前,我实际上写了一个完整的答案

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

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

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


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

最近一次是我在PyCon2010上的演示文稿“”,其中我使用分子式计算器作为示例问题比较了这两个Python解析系统。甚至还有一个

该演示基于我使用ANTLR开发的分子式解析器。在本文中,我将ANTLR解决方案与手工编写的正则表达式解析器以及PLY和PyParsing中的解决方案进行了比较

regexp和PLY解决方案最初是在一个基于Python编写解析器的两种方法的开发环境中开发的

regexp解决方案和基本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)我得到

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


如果您想支持更复杂的公式,比如C6H2(NO2)3CH3,那么您需要了解一些关于树数据结构的知识,特别是(正如@Roman指出的)抽象语法树(通常称为AST)。这太复杂了,无法在这里进行讨论,因此请参阅我的演讲和文章了解更多细节。

我不是化学方面的专家,但我记得,可能会出现更复杂的结构,如(NH4)2SO4,它应该被翻译为[N,N,H,H,H,H,H,s,O,O,O,O,O]。我还记得,那个复杂的化合物看起来很复杂。你应该编写程序来处理这些情况吗?如果你能说出你到底是如何解决这个问题的,以及你试图避免的是什么,那会更有帮助。我有所有其他的代码,我只是想找到一种方法把公式分解成更小的部分。我希望它能够处理多个Parentese层中的嵌套公式。这并不容易:)我已经提供了一个关于另一个问题的优秀解析器的概要:是的,确切地说,我很难处理这些括号。但我可能会将它们分开,在内部运行相同的方法。是的,你必须以某种方式有效地“增加括号”并递归工作。+1用于识别我的TNT公式;遗憾的是,我不能为解决方案提供您应得的+1。H3的最后一个“+”运算符是否应该是“*”运算符?在Wikipedia中,它提到了JUMBO,一个可以处理CML的java库。听起来这比你自己开发一种没有人使用的小型化学表达语言要好。CML在这方面做得太过火了。首先,CML不使用分子公式,而是使用分子图,因此最初的海报需要一个分子公式解析器,以便首先生成CML。这听起来很棒!我在寻找一个不太神秘的解决方案。你能给你的代码贴个链接吗?
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
['C', 'H', 'H', 'H', 'C', 'O', 'O', 'H']