Digamos que tengo una cadena:
teststring = "1.3 Hello how are you 1.4 I am fine, thanks 1.2 Hi There 1.5 Great!"Que me gustaría como:
testlist = ["1.3 Hello how are you", "1.4 I am fine, thanks 1.2 Hi There", "1.5 Great!"]Básicamente, dividir solo en dígitos crecientes donde la diferencia es .1 (es decir, 1.2 a 1.3).
¿Hay alguna manera de dividir esto con expresiones regulares pero solo capturando números secuenciales crecientes? Escribí el código en python para iterar secuencialmente usando un re.compile() personalizado para cada uno y está bien pero es extremadamente difícil de manejar.
Algo como esto (donde parts1_temp es una lista dada de los números xx en la cadena):
parts1_temp = ['1.3','1.4','1.2','1.5'] parts_num = range(int(parts1_temp.split('.')[1]), int(parts1_temp.split('.')[1])+30) parts_search = ['.'.join([parts1_temp.split('.')[0], str(parts_num_el)]) for parts_num_el in parts_num] #parts_search should be ['1.3','1.4','1.5',...,'1.32'] for k in range(len(parts_search)-1): rxtemp = re.compile(r"(?:"+str(parts_search[k])+")([\s\S]*?)(?=(?:"+str(parts_search[k+1])+"))", re.MULTILINE) parts_fin = [match.group(0) for match in rxtemp.finditer(teststring)]Pero hombre, es feo. ¿Hay alguna manera de hacer esto más directamente en expresiones regulares? Me imagino que esta es una característica que alguien hubiera querido en algún momento con expresiones regulares, pero no puedo encontrar ninguna idea sobre cómo abordar esto (y tal vez no sea posible con expresiones regulares puras).
Hacer esto con una expresión regular solo parece demasiado complejo. ¿Qué pasa con este procesamiento:
import re teststring = "1.3 Hello how are you 1.4 I am fine, thanks 1.2 Hi There 1.5 Great!" res = [] expected = None for s in re.findall(r'\d+(?:\.\d+)?|\D+', teststring): if s[0].isdigit() and expected is None: expected = s fmt = '{0:.' + str(max(0, len(s) - (s+'.').find('.') - 1)) + 'f}' inc = float(re.sub(r'\d', '0', s)[0:-1] + '1') if s == expected: res.append(s) expected = fmt.format(float(s) + inc) elif expected: res[-1] = res[-1] + s print (res)Esto también funciona si los números tienen 2 decimales o más, o ninguno.
Este método usa finditer para encontrar todas las ubicaciones de \d+\.\d+ , luego prueba si la coincidencia fue numéricamente mayor que la anterior. Si la prueba es verdadera, agrega el índice a la matriz de indices .
La última línea usa la comprensión de lista tomada de esta respuesta para dividir la cadena en esos índices dados.
Este método asegura que la coincidencia anterior sea más pequeña que la actual. Esto no funciona secuencialmente, sino que funciona en función del tamaño del número. Entonces, suponiendo que una cadena tenga los números 1.1, 1.2, 1.4 , se dividirá en cada aparición ya que cada número es mayor que el anterior.
import re indices = [] string = "1.3 Hello how are you 1.4 I am fine, thanks 1.2 Hi There 1.5 Great!" regex = re.compile(r"\d+\.\d+") lastFloat = 0 for m in regex.finditer(string): x = float(m.group()) if lastFloat < x: lastFloat = x indices.append(m.start(0)) print([string[i:j] for i,j in zip(indices, indices[1:]+[None])]) Salidas: ['1.3 Hello how are you ', '1.4 I am fine, thanks 1.2 Hi There ', '1.5 Great!']
Este método es muy similar al original, sin embargo, en el caso de 1.1, 1.2, 1.4 , no se dividiría en 1.4 ya que no sigue secuencialmente dado el separador secuencial .1 .
El método a continuación solo difiere en la instrucción if , por lo que esta lógica es bastante personalizable según sus necesidades.
import re indices = [] string = "1.3 Hello how are you 1.4 I am fine, thanks 1.2 Hi There 1.5 Great!" regex = re.compile(r"\d+\.\d+") lastFloat = 0 for m in regex.finditer(string): x = float(m.group()) if (lastFloat == 0) or (x == round(lastFloat + .1, 1)): lastFloat = x indices.append(m.start(0)) print([string[i:j] for i,j in zip(indices, indices[1:]+[None])])También puede mutar la cadena para que se coloque un marcador junto al dígito si es parte de la secuencia creciente. Luego, puedes dividir en ese marcador:
import re teststring = "1.3 Hello how are you 1.4 I am fine, thanks 1.2 Hi There 1.5 Great!" numbers = re.findall('[\.\d]+', teststring) final_string = re.sub('[\.\d]+', '{}', teststring).format(*[numbers[0]]+[numbers[i] if numbers[i] < numbers[i-1] else '*'+numbers[i] for i in range(1, len(numbers))]).split(' *')Producción:
['1.3 Hello how are you', '1.4 I am fine, thanks 1.2 Hi There', '1.5 Great!']