2012-03-20 2 views
5

나는 입력 (화학 공식)을 받아서 목록으로 나눠주는 함수 (파이썬에서)를 만들려고한다. 입력이 "HC2H3O2"인 경우 예를 들어, 그것으로 바꿀 것 :문자열을 키워드를 기반으로 목록 요소로 바꾼다

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

이 지금까지 잘 작동하지만 입력하면 예를 들어 나트륨에 대한 그것의 두 글자와 요소 (나)

['N', 'a'] 

요소라는 사전에있는 키의 문자열을 통해 내 함수를 살펴 보는 방법을 찾고 있습니다. 나는 또한 이것에 대한 정규식을 사용하여 고려 중이 야,하지만 그것을 구현하는 방법을 잘 모르겠습니다.

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 

답변

4

에 대한

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

결과

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

정규식 설명 방법 :

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 

이 표현되는 것이 꽤 벙어리이 제 기능은 지금 무엇이다 임의의 "요소" , "Xy"처럼. 당신은, 정규 표현식은

8(NH4)3P4Mo12O40 + 64NaNO3 + 149NH4NO3 + 135H2O 
처럼 뭔가를 구문 분석, 아주 간단한 공식을 처리 할 수 ​​물론 Ba|Na|Mn...|C|O

처럼 |에 의해 분리 된 요소의 이름의 실제 목록으로 [A-Z][a-z]? 부품을 교체하여 개선 할 수 있습니다

예를 들어 실제 파서가 필요할 것입니다. pyparsing ("화학 공식"에서 "예제"를 확인하십시오). 행운을 빕니다!

+0

화려한하다고, 감사합니다! 정규식을 설명해 주시겠습니까? – ohblahitsme

+0

'Ca (HCOO) 2'는 어떻습니까? –

+0

+1 당신이 정규 파서 대신 실제 파서가 필요하다는 것을 언급하기 위해 +1 – aitchnyu

2

이 같은 표현은 관심의 모든 부분을 일치합니다 :

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

당신은 re.findall 함께 사용하고 아무도 없어 원자의 정량을 추가 할 수 있습니다.

또는 당신은뿐만 아니라 그것을 위해 정규식을 사용할 수

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

출력 :

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

sub은 수에 따라 모든 원자 후 1을 추가합니다.

0

아마 최고의 조금 hackish이고 비 정규식 접근 방식,하지만 작동은 :

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'] 
관련 문제