我正在尝试为应用程序编写一个方法,该方法采用类似“CH3COOH”的化学式,并返回一些充满符号的集合。
CH3COOH会返回[C,H,H,H,C,O,O,H]
我已经有了一些有点工作的东西,但它非常复杂并且使用了大量嵌套if-else结构和循环的代码。
有没有办法可以通过使用String.split的某种正则表达式或者其他一些简单的代码来实现这一点?
答案 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");