Разбор химической формулы - PullRequest
21 голосов
/ 04 июня 2010

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

CH3COOH вернет [C, H, H, H, C, O, O, H]

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

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

Ответы [ 5 ]

31 голосов
/ 18 сентября 2010

Я разработал пару статей о том, как анализировать молекулярные формулы, включая более сложные формулы, такие как C6H2 (NO2) 3CH3.

Самой недавней является моя презентация " PLY и PyParsing " на PyCon2010, где я сравниваю эти две системы синтаксического анализа Python, используя в качестве примера моего примера анализатор молекулярных формул. Есть даже видео моей презентации .

Презентация была основана на серии статей , состоящей из трех частей Я разработал парсер молекулярных формул с использованием ANTLR. В part 3 я сравниваю решение ANTLR с рукописным анализатором регулярных выражений и решениями в PLY и PyParsing.

Решения regexp и PLY были впервые разработаны в серии , состоящей из двух частей , для двух способов написания синтаксических анализаторов в 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). ). Это слишком сложно, чтобы попасть сюда, поэтому смотрите мои выступления и эссе для более подробной информации.

24 голосов
/ 04 июня 2010

Предполагая, что оно правильно написано с заглавной буквы, каждый символ в уравнении соответствует этому регулярному выражению:

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

(Для химически оспариваемого символа элемента всегда заглавная буква, за которой следует, возможно, строчная буква один или, возможно, два - например, Hg для ртути)

Вы можете захватить символ элемента и число в группах следующим образом:

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

Так что да, в теории это могло бы помочь с регулярными выражениями. Если вы имеете дело с формулами, такими как C 6 H 2 (NO 2 ) 3 (CH 3 ) 3 тогда ваша работа, конечно, немного сложнее ...

12 голосов
/ 04 июня 2010

Решение с регулярными выражениями - лучший подход, если вам нужно обрабатывать только простые случаи. В противном случае вам нужно создать что-то вроде Абстрактное синтаксическое дерево и оценить его или использовать Польская запись .

Например, формула TNT C6H2(NO2)3CH3 должна быть представлена ​​как:

(+ (* C 6) (* H 2) (* (+ N (* O 2)) 3) C (+ H 3))
4 голосов
/ 04 июня 2010

Вы пытались выразить свои химические формулы на Chemical Markup Language ? Он очень универсален, и существует множество инструментов / средств просмотра, которые могут отображать эти химические форумы или соединения в 2D в 3D.

2 голосов
/ 04 июня 2010

Я работаю над программой, которая требует вычисления молярной массы химических формул, поэтому я создал решение, которое работает с различными формулами.

Например, "(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"); 
...