How to Split by Capital Letters and Numbers at the same time?

Asked

Viewed 141 times

7

How can I separate a string by letters and numbers at the same time?

For example if I have strings:

composto1 = 'H2SO4'
composto2 = 'CaCO2'
composto3 = 'C20H17'

I’ve tried to do:

import re
list_composto1 = re.findall('[A-Z][^A-Z]*', composto1)
list_composto2 = re.findall('[A-Z][^A-Z]*', composto2)
list_composto3 = re.findall('[A-Z][^A-Z]*', composto3)
print(list_composto1 , list_composto2 , list_composto3)

output

['H2', 'S', 'O4'] ['Ca', 'C', 'O2'] ['C20', 'H17']

However, I was only able to separate by the capital letters. Would have some method to separate the numbers together and get the result below:

list_composto1 = ['H', '2', 'S', 'O', '4']
list_composto2 = ['Ca', 'C', 'O' , '2']
list_composto3 = ['C', '20', 'H', '17' ]

2 answers

7


Your criterion is basically:

  • a capital letter followed optionally by a lowercase letter, or
  • one or more numbers

So just do it:

import re
r = re.compile(r'[A-Z][a-z]?|\d+')
composto1 = 'H2SO4'
composto2 = 'CaCO2'
composto3 = 'C20H17'
list_composto1 = r.findall(composto1)
list_composto2 = r.findall(composto2)
list_composto3 = r.findall(composto3)
print(list_composto1 , list_composto2 , list_composto3)

The ? indicates that the lower case letter is optional, and the | means "or". Already the shortcut \d means "one digit", and the quantifier + indicates "one or more".

I also used re.compile, that is recommended when you use the same expression several times.

5

Just out of curiosity.
Also in Python it is possible to perform the same task without resorting to regular expressions, iterating by characters and using a list as pile to separate the parts of the formula.

composto1 = 'SnBrCl3'
composto2 = 'C6H12O6'
composto3 = 'C7H5N3O6'


def qsplit(s):      
  stack = []                              #Inicializa a pilha de separação.
  result = []                             #Inicializa a lista de resultados.
  index= 0                                #Inicializa o índice indicando o caractere sendo iterado. 

  #Sub-rotina que joga os valore da pilha na lista de resultados e esvazia a pilha.
  def pop():
    result.append("".join(stack))         #Joga o valor da pilha na lista de resultados.
    stack.clear()                         #Esvazia a pilha.

  #Itera pelos caracteres da string s...
  while index < len(s):
    #...verifica se o caractere é letra maiúscula...
    if (val:= s[index]).isupper():        
      if len(stack)!=0: pop()             #...se a pilha não estiver vazia chama pop()
      stack.append(val)                   #...adiciona o caractere a pilha.
    #...verifica se o caractere é letra minúscula...
    elif val.islower():
      stack.append(val)                   #...adiciona o caractere a pilha.
      pop()                               #... chama a função pop().
    #...verifica se o caractere é um digito decimal...
    elif val.isdecimal():
      if len(stack) !=0 and not stack[-1].isdecimal(): pop() #...se a pilha não estiver vazia chama pop()
      stack.append(val)                   #...adiciona o caractere a pilha.
    else:
      print(f'Caractere {val} inválido.') #...caso o caractere não enquadre em nenhuma opção. 
    index += 1                            #incrementa o índice do caractere sendo iterado.
  else:
    pop()                                 #ao deixar o laço de iteração chama pop().
  return result

print(qsplit(composto1))                  #['Sn', 'Br', 'Cl', '3']
print(qsplit(composto2))                  #['C', '6', 'H', '12', 'O', '6']
print(qsplit(composto3))                  #['C', '7', 'H', '5', 'N', '3', 'O', '6']

Test the code on Repl.it

Browser other questions tagged

You are not signed in. Login or sign up in order to post.