2012-03-20 14 views
5

Próbuję utworzyć funkcję (w języku Python), która pobiera dane wejściowe (wzór chemiczny) i dzieli się na listę. Na przykład, jeśli sygnał wejściowy jest „HC2H3O2”, to będzie włączyć je do:Przerwij łańcuch na elementy listy na podstawie słów kluczowych

molecule_list = ['H', 1, 'C', 2, 'H', 3, 'O', 2] 

To dobrze do tej pory, ale jeśli wejście element z dwoma literami w tym, na przykład, sód (Na) , byłoby podzielić ją na:

['N', 'a'] 

Szukam sposobu, aby moja funkcja przejrzeć łańcucha kluczy znalezionych w słowniku nazywane elementami. Rozważam również zastosowanie w tym celu wyrażenia regularnego, ale nie jestem pewien, jak go zaimplementować. To właśnie moja funkcja jest teraz:

def split_molecule(inputted_molecule): 
    """Take the input and split it into a list 
    eg: C02 => ['C', 1, 'O', 2] 
    """ 
    # step 1: convert inputted_molecule to a list 
    # step 2a: if there are two periodic elements next to each other, insert a '1' 
    # step 2b: if the last element is an element, append a '1' 
    # step 3: convert all numbers in list to ints 

    # step 1: 
    # problem: it splits Na into 'N', 'a' 
    # it needs to split by periodic elements 
    molecule_list = list(inputted_molecule) 

    # because at most, the list can double when "1" is inserted 
    max_length_of_molecule_list = 2*len(molecule_list) 
    # step 2a: 
    for i in range(0, max_length_of_molecule_list): 
     try: 
      if (molecule_list[i] in elements) and (molecule_list[i+1] in elements): 
       molecule_list.insert(i+1, "1") 
     except IndexError: 
      break 
    # step2b:  
    if (molecule_list[-1] in elements): 
     molecule_list.append("1") 

    # step 3: 
    for i in range(0, len(molecule_list)): 
     if molecule_list[i].isdigit(): 
      molecule_list[i] = int(molecule_list[i]) 

    return molecule_list 

Odpowiedz

4

Jak o

import re 
print re.findall('[A-Z][a-z]?|[0-9]+', 'Na2SO4MnO4') 

wynik

['Na', '2', 'S', 'O', '4', 'Mn', 'O', '4'] 

Regex wyjaśnił:

Find everything that is either 

    [A-Z] # A,B,...Z, ie. an uppercase letter 
    [a-z] # followed by a,b,...z, ie. a lowercase latter 
    ?  # which is optional 
    |  # or 
    [0-9] # 0,1,2...9, ie a digit 
    +  # and perhaps some more of them 

Wyrażenie to jest bardzo głupie, ponieważ akceptuje dowolne "elementy" , jak "Xy". można ją poprawić poprzez zastąpienie [A-Z][a-z]? część z aktualną listą nazw elementów, oddzielonych |, jak Ba|Na|Mn...|C|O

Oczywiście, wyrażenia regularne mogą obsługiwać tylko bardzo proste formuły, aby analizować coś

8(NH4)3P4Mo12O40 + 64NaNO3 + 149NH4NO3 + 135H2O 

będziesz potrzebować prawdziwego analizatora składni, np pyparsing (pamiętaj, aby sprawdzić "wzory chemiczne" w "Przykłady"). Powodzenia!

+0

To genialne, dziękuję! Czy mógłbyś wyjaśnić regex? – ohblahitsme

+0

Co z 'Ca (HCOO) 2'? –

+0

+1 za to, że potrzebujesz prawdziwego analizatora składni zamiast edytora regex – aitchnyu

2

Wyrażenie tak dopasuje wszystkie części odsetek:

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

Można go używać z re.findall a następnie dodaj kwantyfikatora dla atomów, które nie mają.

Albo można użyć wyrażenia regularnego, że dobrze:

molecule = 'NaHC2H3O2' 
print re.findall(r'[A-Z][a-z]*|\d+', re.sub('[A-Z][a-z]*(?![\da-z])', r'\g<0>1', molecule)) 

wyjściowa:

['Na', '1', 'H', '1', 'C', '2', 'H', '3', 'O', '2'] 

sub dodaje 1 po wszystkich atomów nie następuje numer.

0

Non-regex podejście, które jest nieco hackish i prawdopodobnie nie najlepszy, ale to działa:

import string 

formula = 'HC2H3O2Na' 
m_list = list() 
for x in formula: 
    if x in string.lowercase: 
     m_list.append(formula[formula.index(x)-1]+x) 
     _ = m_list.pop(len(m_list)-2) 
    else: 
     m_list.append(x) 
print m_list 
['H', 'C', '2', 'H', '3', 'O', '2', 'Na'] 
Powiązane problemy