¿Cómo acumulo una secuencia de dígitos en una cadena y convertirlos a un número?

votos
4

Necesito para decodificar una cadena 'A3b2' en 'aaabb'. El problema es cuando los números son dobles, triples dígitos. Por ejemplo, 'a10b3' debe detectar que el número es no 1, pero 10.

Tengo que empezar a acumular dígitos.

a = a12345t5i6o2r43e2
for i in range(0, len(a)-1):
  if a[i].isdigit() is False: 
   #once i see a letter, i launch a while loop to check how long a digit streak

   #after it can be - it's 2,3,4,5 digit number etc
    print(a[i])
    current_digit_streak = ''
    counter = i+1
    while a[counter].isdigit():  #this gives index out of range error!
      current_digit_streak += a[counter]
      counter+=1

Si cambio el bucle while para esto:

while a[counter].isdigit() and counter < ( len(a)-1)

No funciona, pero omite la última letra. No debería utilizar expresiones regulares, solo los bucles.

Publicado el 27/11/2018 a las 16:54
fuente por usuario
En otros idiomas...                            


6 respuestas

votos
3

Expresiones regulares es una buena opción aquí.

import re
pat = re.compile(r"""
(\w)       # a word character, followed by...
(\d+)      # one or more digits""", flags=re.X)

s = "a12345t5i6o2r43e2"
groups = pat.findall(s)
# [('a', '12345'), ('t', '5'), ('i', '6'), ('o', '2'), ('r', '43'), ('e', '2')]

result = ''.join([lett*int(count) for lett, count in groups])

Como no se puede utilizar expresiones regulares por alguna razón, sin saberlo, recomiendo una función recursiva para dividir la cadena en partes.

import itertools

def split_into_groups(s):
    if not s:
        return []
    lett, *rest = s
    count, rest = int(itertools.takewhile(str.isdigit, rest)), itertools.dropwhile(str.isdigit, rest)
    return [(lett, count)] + split_into_groups(rest)

s = "a12345t5i6o2r43e2"
groups = split_into_groups(s)

result = ''.join([lett*count for lett, count in groups])

o, usando un patrón más genérico (y derivado funcional):

def unfold(f, x):
    while True:
        v, x = f(x)
        yield v

def get_group(s):
    if not s:
        raise StopIteration()
    lett, *rest = s
    count, rest = int(itertools.takewhile(str.isdigit, rest)), itertools.dropwhile(str.isdigit, rest)
    return lett*count, rest

s = "a12345t5i6o2r43e2"
result = ''.join(unfold(get_group, s))
Respondida el 27/11/2018 a las 16:58
fuente por usuario

votos
2

Se podría utilizar GroupBy :

from itertools import groupby

text = 'a12345t5i6o2r43e2'

groups = [''.join(group) for _, group in groupby(text, key=str.isdigit)]
result = list(zip(groups[::2], groups[1::2]))

print(result)

Salida

[('a', '12345'), ('t', '5'), ('i', '6'), ('o', '2'), ('r', '43'), ('e', '2')]
Respondida el 27/11/2018 a las 17:00
fuente por usuario

votos
1

Esto es un poco largo, pero funciona y utiliza bucles como usted pidió:

def parse_segment(string, index):
    for i, letter in enumerate(string[index+1:]):
        if letter.isalpha():
            return string[index+1:i+index+1]
        if i + index + 1 >= len(string) - 1:
            return string[index+1:]

def segment_string(string):
    num_list = []
    for index, letter in enumerate(string):
        if letter.isalpha():
            num_list.append({'letter': letter, 'number': int(parse_segment(string,  index))})
    return num_list

def list_2_string(list):
    ret_string = ''
    for row in list:
        ret_string += row['letter'] * row['number']
    return ret_string

a = "a12345t5i6o2r43e2"
segmented_string = segment_string(a)
result_string = list_2_string(segmented_string)
print(result_string)
Respondida el 27/11/2018 a las 22:21
fuente por usuario

votos
1

Aquí hay una solución funcional utilizando el itertoolsmódulo. Puede utilizar la grouper receta de los itertoolsdocumentos o la importación a través de 3 ª parte more_itertools.grouper:

from itertools import groupby
from more_itertools import grouper
from operator import itemgetter

a = "a12t5i6o2r11e2"

it = map(''.join, map(itemgetter(1), groupby(a, key=str.isdigit)))

res = ''.join(char*int(count) for char, count in grouper(it, 2))

'aaaaaaaaaaaatttttiiiiiioorrrrrrrrrrree'

Para referencia, la grouperreceta:

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)
Respondida el 27/11/2018 a las 17:21
fuente por usuario

votos
1

Su forbucle y whilebucle utilizan diferentes índices para la obtención de fichas, por lo que los personajes consumidos por el whilebucle se procesan una vez más por el forbucle. En su lugar debe utilizar un whilebucle con un solo índice para analizar las fichas:

a = "a12t5i6o2r11e2"
i = 0
char = repeat = output = ''
while i < len(a):
    token = a[i]
    if token.isdigit():
        repeat += token
    if char and repeat and (not token.isdigit() or i == len(a) - 1):
        output += char * int(repeat)
        char = repeat = ''
    if not token.isdigit():
        char += token
    i += 1
print(output)

Este salidas:

aaaaaaaaaaaatttttiiiiiioorrrrrrrrrrree
Respondida el 27/11/2018 a las 17:18
fuente por usuario

votos
1

Uno de posible variante

import re


def main():
    a = "a10t5i6o2r43e2"
    items = re.findall(r'(\w)(\d+)', a)
    return ''.join([letter*int(count) for letter, count in items])
Respondida el 27/11/2018 a las 17:05
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more