Подтвердить что ты не робот

Размышление о химической формуле

Я пытаюсь написать метод для приложения, которое принимает химическую формулу типа CH3COOH и возвращает какую-то коллекцию, полную их символов.

CH3COOH возвратит [C, H, H, H, C, O, O, H]

У меня уже есть что-то работающее, но оно очень сложно и использует много кода с множеством вложенных структур if и else.

Есть ли способ, которым я могу это сделать, используя какое-то регулярное выражение с String.split или, возможно, в каком-то другом блестящем простом коде?

4b9b3361

Ответ 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");