Размышление о химической формуле
Я пытаюсь написать метод для приложения, которое принимает химическую формулу типа CH3COOH и возвращает какую-то коллекцию, полную их символов.
CH3COOH возвратит [C, H, H, H, C, O, O, H]
У меня уже есть что-то работающее, но оно очень сложно и использует много кода с множеством вложенных структур if и else.
Есть ли способ, которым я могу это сделать, используя какое-то регулярное выражение с String.split или, возможно, в каком-то другом блестящем простом коде?
Ответы
Ответ 1
Предполагая, что он правильно заглавный, каждый символ в уравнении соответствует этому регулярному выражению:
[A-Z][a-z]*\d*
(Для химически оспариваемого символа элемента всегда есть заглавная буква, за которым следует, возможно, нижний регистр один или, возможно, два - например, ртуть для ртути)
Вы можете зафиксировать символ элемента и число в таких группах:
([A-Z][a-z]*)(\d*)
Итак, да, в теории это могло бы помочь с регулярными выражениями. Если вы имеете дело с такими формулами, как C 6 H 2 (NO 2) 3 (CH 3) 3, тогда ваша работа, конечно, немного сложнее...
Ответ 2
Я разработал пару сериалов о том, как анализировать молекулярные формулы, включая более сложные формулы, такие как C6H2 (NO2) 3CH3.
Самая недавняя моя презентация " PLY и PyParsing" в PyCon2010, где я сравниваю эти две синтаксические системы Python с помощью анализатора молекулярных формул как мой образец проблема. Там даже видео моей презентации.
Презентация была основана на трехчастной серии статей. Я разработал синтаксический анализатор молекулярных формул с использованием ANTLR. В часть 3 Я сравниваю решение ANTLR с рукописным парсером регулярных выражений и решениями в PLY и PyParsing.
Режимы регулярного выражения и 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), я получаю
['C', 'H', 'H', 'H', 'C', 'O', 'O', 'H']
Обратите внимание, что этот короткий бит кода предполагает, что молекулярная формула правильная. Если вы дадите ему что-то вроде "## $% ^ O2 # $$ #", тогда он будет игнорировать поля, о которых он не знает, и дать ['O', 'O']. Если вы этого не хотите, вам придется сделать его более надежным.
Если вы хотите поддерживать более сложные формулы, такие как C6H2 (NO2) 3CH3, вам нужно знать немного о структурах древовидных данных, в частности (как указывает @Roman), абстрактных синтаксических деревьях (чаще всего называемых АСТ). Это слишком сложно, чтобы попасть сюда, поэтому см. Мои рассказы и эссе для более подробной информации.
Ответ 3
Решение с регулярными выражениями - лучший подход, если вам нужно обрабатывать только простые случаи. В противном случае вам нужно создать что-то вроде Абстрактное дерево синтаксиса и оценить его или использовать Polish Notation.
Например, формула TNT C6H2(NO2)3CH3
должна быть представлена следующим образом:
(+ (* C 6) (* H 2) (* (+ N (* O 2)) 3) C (+ H 3))
Ответ 4
Вы изучили выражение химических формул в Язык химической маркировки? Он очень универсален, и есть много инструментов/зрителей, которые могут отображать эти химические форумы или соединения в 2D-3D.
Ответ 5
Я работаю над программой, которая требует расчета молярной массы химических формул, поэтому я создал решение, которое работает с множеством формул.
Например, "(CH3) 16 (Tc (H2O) 3CO (BrFe3 (ReCl) 3 (SO4) 2) 2) 2MnO4" приведет к "16C 48H 2Tc 12H 6O 2 C 2 O 4Br 12Fe 12Re 12Cl 8S 32O Mn 4O" (это соединение составлено, но эй, оно работает!)
Этот код написан на С#, поэтому я не разместил его. Если вам интересно, я могу опубликовать его для вас. На самом деле я написал полный ответ, прежде чем замечать тег java.
Во всяком случае, он работает, в основном, группируя блоки атомов, согласованные скобками, рекурсивно. Он не обрабатывает коэффициенты, такие как 2Pb (но (Pb) 2 или Pb2 действительно работает) или заряженные соединения, такие как OH-.
Ни в коем случае это просто или элегантно. Мне действительно нужно рабочее решение, поэтому я знаю, что есть лучшие способы (я даже не пробовал регулярные выражения!). Но он работает с формулами, которые мне нужны, возможно, это подходит и вам.
Вот некоторые тестовые примеры, в которых я запускаю его. Взгляните на них и сообщите мне, если код С# по-прежнему будет вам полезен. Формат (вход, ожидаемый вывод)
("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");