Expressions rationnelles

Le
Pierre Maurette
Bonjour,

C'est encore moi, je m'incruste un peu.

Voilà, je suis dans un trip "toute fonction de plus d'une ligne est une
fonction trop longue". Je m'amuse bien, mon code s'est tassé en hauteur
mais a prospéré au-delà du raisonnable en largeur et en inbitabilité.
Je vais peut-être me calmer un peu.

Le but est de traiter une liste éditable d'extensions, "ini ; .dat ;
_at ,1a ; 2 ; exe ;" par exemple, qui doit donner ['ini', 'dat', '_at',
'1a', '2', 'exe'], l'utilisateur étant supposé avoir été bercé très
près d'un mur.

Au début j'avais fait ça:
s.replace(';', ' ').replace(',', ' ').replace('-', ' ').split()
qui donnait satisfaction (avec un utilisateur un peu attentif
toutefois).

J'ai voulu ensuite utiliser RE:
re.split('[W]+', s)
Compact, sans doute moins performant, mais qui me laisse des '' en fin
de liste (et éventuellement au début).

Je corrige comme ça:
re.split('[W]+', s.strip(';,- '))

Mais je préfèrerais une solution entièrement RE. Et c'est là que je
coince. Je ne peux le faire que lourdement:
re.split('[W]+', re.sub('A[W]+|[W]+$','', s))

Il doit bien y avoir un truc plus compact, non ?

Bonne journée

--
Pierre Maurette
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Cémoi
Le #601965
Bonjour,

C'est encore moi, je m'incruste un peu.

Voilà, je suis dans un trip "toute fonction de plus d'une ligne est une
fonction trop longue". Je m'amuse bien, mon code s'est tassé en hauteur
mais a prospéré au-delà du raisonnable en largeur et en inbitabilité. Je
vais peut-être me calmer un peu.

Le but est de traiter une liste éditable d'extensions, "ini ; .dat ; _at
,1a ; 2 ; exe ;" par exemple, qui doit donner ['ini', 'dat', '_at',
'1a', '2', 'exe'], l'utilisateur étant supposé avoir été bercé très près
d'un mur.

Au début j'avais fait ça:
s.replace(';', ' ').replace(',', ' ').replace('-', ' ').split()
qui donnait satisfaction (avec un utilisateur un peu attentif toutefois).

J'ai voulu ensuite utiliser RE:
re.split('[W]+', s)
Compact, sans doute moins performant, mais qui me laisse des '' en fin
de liste (et éventuellement au début).

Je corrige comme ça:
re.split('[W]+', s.strip(';,- '))

Mais je préfèrerais une solution entièrement RE. Et c'est là que je
coince. Je ne peux le faire que lourdement:
re.split('[W]+', re.sub('A[W]+|[W]+$','', s))

Il doit bien y avoir un truc plus compact, non ?


re.findall ('[w]+', s)


Bonne journée...


Pareil...



Pierre Maurette
Le #601964

[...]

Il doit bien y avoir un truc plus compact, non ?


re.findall ('[w]+', s)


Super ! Merci ...

C'était juste en dessous de split dans la doc, mais je n'avais pas
percuté.

--
Pierre Maurette


maric
Le #601963


Voilà, je suis dans un trip "toute fonction de plus d'une ligne est
une fonction trop longue". Je m'amuse bien, mon code s'est tassé en
hauteur mais a prospéré au-delà du raisonnable en largeur et en
inbitabilité. Je vais peut-être me calmer un peu.

Le but est de traiter une liste éditable d'extensions, "ini ; .dat ;
_at ,1a ; 2 ; exe ;" par exemple, qui doit donner ['ini', 'dat',
'_at', '1a', '2', 'exe'], l'utilisateur étant supposé avoir été bercé
très près d'un mur.

Au début j'avais fait ça:
s.replace(';', ' ').replace(',', ' ').replace('-', ' ').split()
qui donnait satisfaction (avec un utilisateur un peu attentif toutefois).

J'ai voulu ensuite utiliser RE:
re.split('[W]+', s)
Compact, sans doute moins performant, mais qui me laisse des '' en fin
de liste (et éventuellement au début).

Je corrige comme ça:
re.split('[W]+', s.strip(';,- '))

Mais je préfèrerais une solution entièrement RE. Et c'est là que je
coince. Je ne peux le faire que lourdement:
re.split('[W]+', re.sub('A[W]+|[W]+$','', s))

Il doit bien y avoir un truc plus compact, non ?


re.findall ('[w]+', s)



Bien, les regexp c'est rapide, pour sûr, mais il y a beaucoup de monde
(dont je fais parti) qui rechigne à les utiliser car elles manquent de
souplesse et il est souvent difficile de les faire évoluer même pour
matcher une chaine proche de l'original.

exemple (j'ajoute le support des caractères français)

In [1]: import locale

In [3]: locale.setlocale(locale.LC_ALL, '')
Out[3]: 'French_France.1252'

In [4]: s = "Une chaîne (avec des caractères français)
!".decode('cp850').encode('iso8859-1') # ça c'est parce que je tape dans
une console dos, c'est un peu idiot

Version regexp :

In [2]: import re

In [5]: re.findall('(?L)w+', s)
Out[5]: ['Une', 'cha?ne', 'avec', 'des', 'caract?res', 'fran?ais']

Version python :

In [21]: import string

In [28]: ''.join(e if e in string.letters else ' ' for e in s).split()
Out[28]: ['Une', 'cha?ne', 'avec', 'des', 'caract?res', 'fran?ais']


Il y a une petite différence car w matche '_', mais ce n'est pas une
lettre, ajoutons-le donc :

In [29]: ''.join(e if e in string.letters + '_' else ' ' for e in s).split()
Out[29]: ['Une', 'cha?ne', 'avec', 'des', 'caract?res', 'fran?ais']

Ici, clairement l'expression régulière va bien, elle est simple courte,
on est juste obligé de switcher d'un langage à l'autre quand on lit le
code, mais admettons qu'on soit à l'aise avec les deux.

Admettons maintenant qu'on veuille faire évoluer notre code vers un
autre ensemble que les caractères 'mot', disons tout sauf la ponctuation.


Version python :


In [27]: ''.join(e if e not in string.punctuation else ' ' for e in
s).split()
Out[27]: ['Une', 'cha?ne', 'avec', 'des', 'caract?res', 'fran?ais']


Version regex :


In [20]: re.findall('(?L)[^%ss]+' % string.punctuation, s)
Out[20]: ['Une', 'cha?ne', 'avec', 'des', 'caract?res', 'fran?ais']


Le passage d'une expression à l'autre est beaucoup plus immédiat en python.
Pire encore, on veut maintenant garder la ponctuation, mais pas les
espaces :

Version python :

In [38]: ''.join(e if e not in string.punctuation else ' %s ' % e for e
in s).split()
Out[38]: ['Une', 'cha?ne', '(', 'avec', 'des', 'caract?res', 'fran?ais',
')', '!']


Version regexp :


In [16]: re.findall('(?L)[%s]|[^%ss]+' % ((string.punctuation,)*2), s)
Out[16]: ['Une', 'cha?ne', '(', 'avec', 'des', 'caract?res', 'fran?ais',
')', '!']


C'est la traduction exacte (attention ici string.punctuation doit
pouvoir être n'importe quel ensemble de caractères). Cela commence à ne
plus ressembler du tout à l'expression de départ

Toujours en restant sur quelque chose de conceptuellement très proche,
disons qu'on veuille garder les caractère de ponctuation, couper après,
mais pas avant.

Version python :

In [13]: ''.join(e if e not in string.punctuation else '%s ' % e for e
in s).split()
Out[13]: ['Une', 'cha?ne', '(', 'avec', 'des', 'caract?res',
'fran?ais)', '!']

Version regex :

In [15]: re.findall('(?L)[^s%s]*[%s]|[^%ss]+' % ((string.punctuation,)
* 3), s)
Out[15]: ['Une', 'cha?ne', '(', 'avec', 'des', 'caract?res',
'fran?ais)', '!']


J'ai souvent entendu dire que les expressions rationnelles sont à usage
unique, du code jetable, et c'est vrai que dès qu'elle deviennent un
tantinet plus complexes que celles-ci, on se décourage vite d'essayer de
les lire, en général on repart de zéro pour les corriger ou les modifier.


MCI, Shadok Gouroudoudou
Le #625794
Bonsoir !

Il existe une maxime qui va dans le même sens :
"si, pour régler un problème, on veut utiliser les expressions
régulières, on se retrouve avec yun deuxième problème."

Et, c'est vrai que la lisibilité des RegEx, ... c'est pas ça. En plus,
il y a plein de notions sous-jacentes (avidité, parenthèses capturantes
ou non, rétro-reconnaissance, etc.)

Quand on a gouté à la lisibilité Python, la réticence guette.





--
@-salutations

Michel Claveau
Publicité
Poster une réponse
Anonyme