2010-06-04 33 views
20

Estoy tratando de escribir un método para una aplicación que toma una fórmula química como "CH3COOH" y devuelve algún tipo de colección llena de sus símbolos.Analizando una fórmula química

CH3COOH volvería [C, H, H, H, C, O, O, H]

ya tengo algo que está trabajando un poco, pero es muy complicado y utiliza una gran cantidad de código con mucha de estructuras y bucles if-else anidados.

¿Hay alguna manera de hacerlo mediante el uso de algún tipo de expresión regular con String.split o tal vez en algún otro código simple brillante?

+3

No soy un gran experto en química pero, como recuerdo, pueden existir construcciones más complicadas, como (NH4) 2SO4, que se debe traducir a [N, N, H, H, H, H, H, H, H, H, S, O, O, O, O]. También recuerdo que ese complejo * complejo parece muy complicado. ¿Deberías programar manejar estos casos? – Roman

+0

Sería más útil si pudieras decir exactamente cómo estás resolviendo este problema y qué es lo que estás tratando de evitar. – MAK

+0

Tengo todo el otro código, solo estoy tratando de encontrar una manera de dividir la fórmula en bits más pequeños. Quiero que sea capaz de trabajar en fórmulas anidadas en varias capas de parenteses. Simplemente no es fácil :) –

Respuesta

23

asumiendo que es correctamente mayúscula, cada símbolo en la ecuación coincide con esta expresión regular:

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

(para personas con problemas químicamente, símbolo de un elemento es siempre letra mayúscula seguida por opcionalmente una caja inferior uno o posiblemente dos - por ejemplo, para el mercurio Hg)

puede capturar el símbolo del elemento y el número de grupos, así:

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

así que sí, en teoría, esto sería algo con lo que las expresiones regulares podrían ayudar. Si usted está tratando con fórmulas como C H 2 (NO 2 ) (CH) entonces su trabajo es, por supuesto, un poco más difícil ...

+0

+1, la misma respuesta que se me ocurrió. –

+0

Sí, exactamente, me costó mucho lidiar con esos paréntesis. Pero probablemente podría separarlos y ejecutar el mismo método por dentro. –

+0

Sí, tendría que "multar de manera efectiva los paréntesis" de alguna manera y trabajar recursivamente. –

12

La solución con expresiones regulares es el mejor enfoque si necesita manejar solo casos simples. De lo contrario, debe construir algo como Abstract Syntax Tree y evaluarlo o utilizar Polish Notation.

Por ejemplo, TNT fórmula C6H2(NO2)3CH3 debe presentarse como:

(+ (* C 6) (* H 2) (* (+ N (* O 2)) 3) C (+ H 3)) 
+12

Esta solución es dinamita – Freiheit

+0

+1 para detectar mi fórmula TNT; lamentablemente, no puedo darte el +1 que también te mereces por la solución. –

4

¿Ha mirado en la expresión de sus fórmulas químicas en Chemical Markup Language? Es muy versátil y hay muchas herramientas/lectores que pueden renderizar estas fórmulas o compuestos químicos en 2D a 3D.

+2

En Wikipedia menciona JUMBO, una biblioteca de Java que puede procesar CML. Parece que eso sería mejor que cultivar tu propio mini lenguaje de expresiones químicas que nadie más usa. –

+0

+1 por no reinventar la cosa redonda. – Carl

+1

CML es demasiado exagerado para esto. Por un lado, CML no utiliza fórmulas moleculares sino gráficos moleculares, por lo que el póster original necesitaría un analizador de fórmulas moleculares solo para generar el CML en primer lugar. –

1

Estoy trabajando en un programa que requiere cálculos de masa molar de fórmulas químicas, por lo que he creado una solución que funciona con una variedad de fórmulas.

Por ejemplo, "(CH3) 16 (Tc (H2O) 3CO (BrFe3 (ReCl _) 3 (SO4) 2) 2) 2MnO4" resultará en "16C 48H 2TC 12H 6O 2C 2O 4Br 12FE 12Re 12CL 8S 32O Mn 4O "(este compuesto está compuesto, pero bueno, ¡funciona!)

Este código está escrito en C# por eso no lo he publicado. Si está interesado, puedo publicarlo para usted. De hecho, escribí una respuesta completa antes de notar la etiqueta java.

De todos modos, funciona básicamente agrupando bloques de átomos combinados por paréntesis recursivamente. No maneja coeficientes como 2Pb (pero (Pb) 2 o Pb2 funciona) o compuestos cargados como OH-.

De ninguna manera es simple o elegante. Quería una solución de trabajo, así que sé que hay formas mejores (¡ni siquiera probé expresiones regulares!). Pero funciona con las fórmulas que necesito, tal vez también se adapte a la tuya.

Aquí hay algunos casos de prueba en los que lo ejecuto. Eche un vistazo a ellos y avíseme si el código C# aún le sería útil. El formato es (entrada, salida esperada)

 ("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"); 
30

he desarrollado un par de series de artículos sobre cómo analizar fórmulas moleculares, incluyendo fórmulas más complejas como C6H2 (NO2) 3CH3.

La más reciente es mi presentación "PLY and PyParsing" en PyCon2010 donde comparo esos dos sistemas de análisis Python utilizando un evaluador de fórmula molecular como mi problema de muestra. Incluso hay un video of my presentation.

La presentación se basó en un three-part series of articles desarrollé un analizador de fórmulas moleculares usando ANTLR. En part 3 comparo la solución ANTLR con un analizador de expresiones regulares escrito a mano y soluciones en PLY y PyParsing.

Las soluciones regexp y PLY se desarrollaron por primera vez en un two-part series en dos formas de escribir analizadores en Python.

La solución regexp y las soluciones ANTLR/PLY/PyParsing base, usan una expresión regular como [A-Z] [a-z]? \ D * para que coincidan con los términos de la fórmula. Esto es lo que @David M sugirió.

Aquí es funcionó en 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 

Cuando ejecuto esto (que es codificada a utilizar el ácido acético, CH3COOH) consigo

['C', 'H', 'H', 'H', 'C', 'O', 'O', 'H'] 

Ten en cuenta que este breve poco de el código asume que la fórmula molecular es correcta. Si le da algo como "## $%^O2 # $$ #", ignorará los campos que desconoce y dará ['O', 'O']. Si no quieres eso, entonces tendrás que hacerlo un poco más robusto.

Si desea admitir fórmulas más complicadas, como C6H2 (NO2) 3CH3, necesitará conocer un poco sobre estructuras de datos de árbol, específicamente (como @Roman señala), árboles sintácticos abstractos (más comúnmente llamados AST). Eso es demasiado complicado para entrar aquí, así que vea mi charla y ensayos para más detalles.

Cuestiones relacionadas