¿Hay alguna manera de hacer coincidir un patrón (e\d\d) varias veces, capturando cada uno en un grupo? Por ejemplo, dada la cadena ..

blah.s01e24e25

.. Deseo obtener cuatro grupos:

1 -> blah
2 -> 01
3 -> 24
4 -> 25

La expresión regular obvia para usar es (en Python regex:

import re
re.match("(\w+).s(\d+)e(\d+)e(\d+)", "blah.s01e24e25").groups()

..pero también quiero coincidir con cualquiera de los siguientes:

blah.s01e24
blah.s01e24e25e26

Parece que no puedes hacer (e\d\d)+, o más bien puedes hacerlo, pero solo captura la última aparición:

>>> re.match("(\w+).s(\d+)(e\d\d){2}", "blah.s01e24e25e26").groups()
('blah', '01', 'e25')
>>> re.match("(\w+).s(\d+)(e\d\d){3}", "blah.s01e24e25e26").groups()
('blah', '01', 'e26')

Quiero hacer esto en una sola expresión regular porque tengo múltiples patrones para que coincidan con los nombres de archivo de episodios de TV, y no quiero duplicar cada expresión para manejar múltiples episodios:

\w+\.s(\d+)\.e(\d+) # matches blah.s01e01
\w+\.s(\d+)\.e(\d+)\.e(\d+) # matches blah.s01e01e02
\w+\.s(\d+)\.e(\d+)\.e(\d+)\.e(\d+) # matches blah.s01e01e02e03

\w - \d+x\d+ # matches blah - 01x01
\w - \d+x\d+\d+ # matches blah - 01x01x02
\w - \d+x\d+\d+\d+ # matches blah - 01x01x02x03

... y así sucesivamente para muchos otros patrones.

Otra cosa que complica las cosas: deseo almacenar estas expresiones regulares en un archivo de configuración, por lo que no es deseable una solución que use múltiples expresiones regulares y llamadas a funciones, pero si esto resulta imposible, solo permitiré al usuario agregar expresiones regulares.

Básicamente, ¿hay alguna manera de capturar un patrón repetitivo usando regex?

4
dbr 27 jun. 2009 a las 23:45

5 respuestas

La mejor respuesta

Después de pensar en el problema, creo que tengo una solución más simple, usar grupos con nombre.

La expresión regular más simple que un usuario (o yo) podría usar es:

(\w+\).s(\d+)\.e(\d+)

La clase de análisis de nombre de archivo tomará el primer grupo como el nombre del programa, el segundo como número de temporada, el tercero como número de episodio. Esto cubre la mayoría de los archivos.

Permitiré algunos grupos con nombre diferentes para estos:

(?P<showname>\w+\).s(?P<seasonnumber>\d+)\.e(?P<episodenumber>\d+)

Para admitir múltiples episodios, apoyaré dos grupos con nombre, algo así como startingepisodenumber y endingepisodenumber para admitir cosas como showname.s01e01-03:

(?P<showname>\w+\)\.s(?P<seasonnumber>\d+)\.e(?P<startingepisodenumber>\d+)-(?P<endingepisodenumber>e\d+)

Y finalmente, permita grupos con nombres que coincidan con episodenumber\d+ (episodenumber1, episodenumber2, etc.):

(?P<showname>\w+\)\.
s(?P<seasonnumber>\d+)\.
e(?P<episodenumber1>\d+)
e(?P<episodenumber2>\d+)
e(?P<episodenumber3>\d+)

Todavía requiere posiblemente duplicar los patrones para diferentes cantidades de e01 s, pero nunca habrá un archivo con dos episodios no consecutivos (como show.s01e01e03e04), por lo que usar los grupos starting/endingepisodenumber resuelva esto, y en casos extraños que los usuarios encuentren, pueden usar los nombres de grupo episodenumber\d+

¡Esto realmente no responde la pregunta de la secuencia de patrones, pero resuelve el problema que me llevó a preguntar! (Todavía aceptaré otra respuesta que muestre cómo hacer coincidir s01e23e24...e27 en una expresión regular, ¡si alguien resuelve esto!)

0
dbr 27 jun. 2009 a las 20:32

Número de grupos capturados igual al número de grupos de paréntesis. Mire findall o finditer para resolver su problema.

1
Rorick 27 jun. 2009 a las 19:56

Paréntesis no agrupados: (?: asdfasdg)

Que no tienen que aparecer: (?: adsfasdf)?

c = re.compile(r"""(\w+).s(\d+)
                       (?:
                            e(\d+)
                            (?:
                                  e(\d+)
                            )?
                        )?
               """, re.X)

O

c = re.compile(r"""(\w+).s(\d+)(?:e(\d+)(?:e(\d+))?)?""", re.X)
1
Adrian Panasiuk 27 jun. 2009 a las 20:18

Hazlo en dos pasos, uno para encontrar todos los números y luego uno para dividirlos:

import re

def get_pieces(s):
    # Error checking omitted!
    whole_match = re.search(r'\w+\.(s\d+(?:e\d+)+)', s)
    return re.findall(r'\d+', whole_match.group(1))

print get_pieces(r"blah.s01e01")
print get_pieces(r"blah.s01e01e02")
print get_pieces(r"blah.s01e01e02e03")

# prints:
# ['01', '01']
# ['01', '01', '02']
# ['01', '01', '02', '03']
5
RichieHindle 27 jun. 2009 a las 19:53

¿Quizás algo así?

def episode_matcher(filename):
    m1= re.match(r"(?i)(.*?)\.s(\d+)((?:e\d+)+)", filename)
    if m1:
        m2= re.findall(r"\d+", m1.group(3))
        return m1.group(1), m1.group(2), m2
    # auto return None here

>>> episode_matcher("blah.s01e02")
('blah', '01', ['02'])
>>> episode_matcher("blah.S01e02E03")
('blah', '01', ['02', '03'])
0
tzot 28 jun. 2009 a las 01:11