Regex for retrieve range of text in python

I'm trying to split a long text of a Norm and extract specific range of text that are the articles of that Norm. I found that using re.split() in python is the best way (I think) to make the job. So here is an example of the text (Spanish) that I'm trying to split

test = "11.3.2 Debe llevarse a cabo mediante equipos o sistemas de seguridad que eviten 
la explosión por golpe, chispa o calentamiento. 12. Requisitos de seguridad e 
higiene para el transporte y almacenamiento de sustancias corrosivas, irritantes 
o tóxicas 12.1 El almacenamiento de sustancias corrosivas, irritantes o tóxicas debe 
hacerse en recipientes específicos, de materiales compatibles con la sustancia de 
que se trate. 12.2 Cuando el transporte de sustancias corrosivas, irritantes o 
tóxicas en los centros de trabajo se realice a través de un sistema de tuberías o 
recipientes portátiles, éstos deben estar cerrados para evitar que su contenido se 
derrame o fugue."

So what I want to achieve is to split the text and have an array of the articles. ['11.3.2 Debe llevarse ...','12. Requisitos ...','12.1 El almacenamiento ...','12.2 Cuando...'].

So currently what I've done with no success is:

re.split("\s(\d{1,2}\.\d*\.*\d*\s[A-Z]+)",test)

As a result:

['11.3.2 Debe llevarse a cabo ...', '12. R', 'equisitos de seg ...', '12.1 E', 'l almacenamiento de sustancias corrosivas ... ', '12.2 C', 'uando el transporte de sustancias corrosivas ...', '13. V', 'igilancia La vigilancia del...']

Any suggestions?

Answers

You could do the following:

import re

test = """11.3.2 Debe llevarse a cabo mediante equipos o sistemas de seguridad que eviten 
la explosión por golpe, chispa o calentamiento. 12. Requisitos de seguridad e 
higiene para el transporte y almacenamiento de sustancias corrosivas, irritantes 
o tóxicas 12.1 El almacenamiento de sustancias corrosivas, irritantes o tóxicas debe 
hacerse en recipientes específicos, de materiales compatibles con la sustancia de 
que se trate. 12.2 Cuando el transporte de sustancias corrosivas, irritantes o 
tóxicas en los centros de trabajo se realice a través de un sistema de tuberías o 
recipientes portátiles, éstos deben estar cerrados para evitar que su contenido se 
derrame o fugue."""

pattern = re.compile('\d{1,2}(\.\d{1,2})*([^\d]+)')


for match in pattern.finditer(test):
    print(match.group())
    print('-----------')

Output

11.3.2 Debe llevarse a cabo mediante equipos o sistemas de seguridad que eviten 
la explosión por golpe, chispa o calentamiento. 
-----------
12. Requisitos de seguridad e 
higiene para el transporte y almacenamiento de sustancias corrosivas, irritantes 
o tóxicas 
-----------
12.1 El almacenamiento de sustancias corrosivas, irritantes o tóxicas debe 
hacerse en recipientes específicos, de materiales compatibles con la sustancia de 
que se trate. 
-----------
12.2 Cuando el transporte de sustancias corrosivas, irritantes o 
tóxicas en los centros de trabajo se realice a través de un sistema de tuberías o 
recipientes portátiles, éstos deben estar cerrados para evitar que su contenido se 
derrame o fugue.
-----------

The pattern \d{1,2}(\.\d{1,2})*([^\d]+) will match the header (the numbering) followed by everything that is not a number. As an alternative you could use the following, with a lookahead:

pattern = re.compile('\d{1,2}(\.\d{1,2})*(.+?)(?=(\d{1,2}(\.\d{1,2})*|$))', re.DOTALL)


for match in pattern.finditer(test):
    print(match.group())
    print('-----------')

Output

11.3.2 Debe llevarse a cabo mediante equipos o sistemas de seguridad que eviten 
la explosión por golpe, chispa o calentamiento. 
-----------
12. Requisitos de seguridad e 
higiene para el transporte y almacenamiento de sustancias corrosivas, irritantes 
o tóxicas 
-----------
12.1 El almacenamiento de sustancias corrosivas, irritantes o tóxicas debe 
hacerse en recipientes específicos, de materiales compatibles con la sustancia de 
que se trate. 
-----------
12.2 Cuando el transporte de sustancias corrosivas, irritantes o 
tóxicas en los centros de trabajo se realice a través de un sistema de tuberías o 
recipientes portátiles, éstos deben estar cerrados para evitar que su contenido se 
derrame o fugue.
-----------

The idea is to match everything that is followed by a header or the end of the text. Note the use of the flag re.DOTALL.

Posted on by Dani Mesejo

Another solution with findall:

re.findall(r"(?s)(?:\d+\.\s|(?:\d+\.)+\d+\s)(?:(?!\d+\.).)+",txt)

Out: 
['11.3.2 Debe llevarse a cabo mediante equipos o sistemas de seguridad que eviten \nla explosión por golpe, chispa o calentamiento.',
 '12. Requisitos de seguridad e \nhigiene para el transporte y almacenamiento de sustancias corrosivas, irritantes \no tóxicas',
 '12.1 El almacenamiento de sustancias corrosivas, irritantes o tóxicas debe \nhacerse en recipientes específicos, de materiales compatibles con la sustancia de \nque se trate.',
 '12.2 Cuando el transporte de sustancias corrosivas, irritantes o \ntóxicas en los centros de trabajo se realice a través de un sistema de tuberías o \nrecipientes portátiles, éstos deben estar cerrados para evitar que su contenido se \nderrame o fugue.']

"""
(?s) '.' matches \n, too

(?:\d+\.\s|(?:\d+\.)+\d+\s)  the exact numbering pattern

(?:(?!\d+\.).)+  matches any character which not a start of a numbering

Edit:
(?:.(?!\d+\.))+  changed to (?:(?!\d+\.).)+
The previous pattern cuts the last char (in our case the space) before the numbering.
"""
Posted on by kantal

I suggest split by the number of the norms and then join the pieces 2 by 2

x = re.split(r"(?:\A\s*|\.\s+)(\d+(?:(?:[.]\d+)+|[.]))", test_str)[1:]
list = [i+j for i,j in zip(x[::2], x[1::2])]
print(list)

See demo here

Posted on by Julio

Relevant tags