2010-08-06 4 views
1

Trois underscore éléments séparés font mes cordes: - premières (lettres et chiffres) - moyen (lettres, chiffres et tirets) - dernières (lettres et chiffres)Trouver le dernier groupe dans une expression régulière

Les le dernier élément est facultatif.

Note: J'ai besoin d'accéder à mes groupes par leurs noms, pas leurs indices.

Exemples:

String : abc_def 
first : abc 
middle : def 
last : None 

String : abc_def_xyz 
first : abc 
middle: def 
last: xyz 

String : abc_def_ghi_jkl_xyz 
first : abc 
middle : def_ghi_jkl 
last : xyz 

Je ne trouve pas le droit ... regex

J'ai deux idées à ce jour:

groupe en option

(?P<first>[a-z]+)_(?P<middle>\w+)(_(?P<last>[a-z]+))? 

Mais les matches de groupe du milieu jusqu'à la fin de la chaîne:

String : abc_def_ghi_jkl_xyz 
first : abc 
middle : def_ghi_jkl_xyz 
last : vide 

Utilisation du '|'

(?P<first>[a-z]+)_(?P<middle>\w+)_(?P<last>[a-z]+)|(?P<first>[a-z]+)_(?P<middle>\w+) 

Cette expression est invalide: premier et les groupes intermédiaires sont déclarés deux fois. Je croyais que je pourrais écrire une expression réutilisant le groupe apparié de la première partie de l'expression:

(?P<first>[a-z]+)_(?P<middle>\w+)_(?P<last>[a-z]+)|(?P=first)_(?P=middle) 

L'expression est valable, mais les chaînes avec juste un premier et un milieu comme abc_def ne correspondent pas.

Remarque

Ces chaînes sont en fait des parties d'un chemin que je dois correspondre. Il pourrait être des chemins comme:

  • /chemin/vers/à/abc_def
  • /ma/chemin/vers/abc_def/
  • /ma/chemin/vers/abc_def/some/autre/Stuf
  • /ma/chemin/vers/abc_def/some/autre/Stuf/
  • /ma/chemin/vers/abc_def_ghi_jkl_xyz
  • /ma/chemin/vers/abc_def_ghi_jkl_xyz/
  • /ma/chemin/vers/abc_def_ghi_jkl_xyz/some/other/stuf
  • /ma/chemin/vers/abc_def_ghi_jkl_xyz/some/autre/Stuf/
  • ...

Toute idée pour résoudre mon problème uniquement avec des expressions régulières? Le post-traitement des groupes appariés n'est pas une option.

Merci beaucoup!

+0

Est-ce que '/ my/path/to /' est constant ou variable? Si variable, comment savez-vous quelle partie du chemin contient la chaîne dont vous essayez d'extraire les sous-chaînes? –

Répondre

3

Changer le groupe moyen d'être non-gourmand, et ajouter de début et de fin de chaîne d'ancrage:

^(?P<first>[a-z]+)_(?P<middle>\w+?)(_(?P<last>[a-z]+))?$ 

Par défaut, le \w+ correspondra à autant beaucoup plus que possible, qui mange le reste de la chaîne. Ajoutant le ? lui dit pour correspondre petit que possible.

Merci à Tim Pietzcker pour avoir souligné les exigences d'ancrage.

+1

Cela ne fonctionnera pas sans ancres.Dans 'abc_def_ghi_jkl_xyz',' first' va correspondre 'abc',' middle' va correspondre 'd', et' last' sera vide. –

+0

@Tim: Vous avez raison. J'ai mis à jour ma réponse. Merci –

1

Utilisez

^(?P<first>[a-z]+)_(?P<middle>\w+?)(_(?P<last>[a-z]+))?$ 

^ et $ ancre la regex au début et à la fin de la chaîne. Rendre le \w+? paresseux lui permet de correspondre le moins possible (mais au moins un caractère).

EDIT:

Pour vos besoins ont changé qui incluent maintenant les chemins avant et après cette chaîne, cela fonctionne:

^(.*?/)(?P<first>[a-z]+)_(?P<middle>\w+?)(_(?P<last>[a-z]+))?(/.*)?$ 
Exemple de code

(Python 3.1):

import re 
paths = ["/my/path/to/abc_def", 
     "/my/path/to/abc_def/", 
     "/my/path/to/abc_def/some/other/stuf", 
     "/my/path/to/abc_def/some/other/stuf/", 
     "/my/path/to/abc_def_ghi_jkl_xyz", 
     "/my/path/to/abc_def_ghi_jkl_xyz/", 
     "/my/path/to/abc_def_ghi_jkl_xyz/some/other/stuf", 
     "/my/path/to/abc_def_ghi_jkl_xyz/some/other/stuf/"] 

regex = re.compile(r"^(.*?/)(?P<first>[a-z]+)_(?P<middle>\w+?)(_(?P<last>[a-z]+))?(/.*)?$") 

for path in paths: 
    match = regex.match(path) 
    print ("{}:\nBefore: {}\nFirst: {}\nMiddle: {}\nLast: {}\nAfter: {}\n".format(
      path, match.group(1), match.group("first"), match.group("middle"), 
      match.group("last"), match.group(6))) 

sortie:

/my/path/to/abc_def: 
Before: /my/path/to/ 
First: abc 
Middle: def 
Last: None 
After: None 

/my/path/to/abc_def/: 
Before: /my/path/to/ 
First: abc 
Middle: def 
Last: None 
After:/

/my/path/to/abc_def/some/other/stuf: 
Before: /my/path/to/ 
First: abc 
Middle: def 
Last: None 
After: /some/other/stuf 

/my/path/to/abc_def/some/other/stuf/: 
Before: /my/path/to/ 
First: abc 
Middle: def 
Last: None 
After: /some/other/stuf/ 

/my/path/to/abc_def_ghi_jkl_xyz: 
Before: /my/path/to/ 
First: abc 
Middle: def_ghi_jkl 
Last: xyz 
After: None 

/my/path/to/abc_def_ghi_jkl_xyz/: 
Before: /my/path/to/ 
First: abc 
Middle: def_ghi_jkl 
Last: xyz 
After:/

/my/path/to/abc_def_ghi_jkl_xyz/some/other/stuf: 
Before: /my/path/to/ 
First: abc 
Middle: def_ghi_jkl 
Last: xyz 
After: /some/other/stuf 

/my/path/to/abc_def_ghi_jkl_xyz/some/other/stuf/: 
Before: /my/path/to/ 
First: abc 
Middle: def_ghi_jkl 
Last: xyz 
After: /some/other/stuf/ 
0

Essayez cette expression régulière:

^(?P<first>[a-z]+)_(?P<middle>[a-z]+(?:_[a-z]+)*?)(?:_(?P<last>[a-z]+))?$ 

est ici un test:

import re 

strings = ['abc_def', 'abc_def_xyz', 'abc_def_ghi_jkl_xyz'] 
pattern = '^(?P<first>[a-z]+)_(?P<middle>[a-z]+(?:_[a-z]+)*?)(?:_(?P<last>[a-z]+))?$' 
for string in strings: 
    m = re.match(pattern, string) 
    print m.groupdict() 

La sortie est:

{'middle': 'def', 'last': None, 'first': 'abc'} 
{'middle': 'def', 'last': 'xyz', 'first': 'abc'} 
{'middle': 'def_ghi_jkl', 'last': 'xyz', 'first': 'abc'} 
+0

J'aurais dû ajouter que mes cordes font partie d'une chaîne plus grande, donc je ne peux pas vraiment utiliser l'ancre $. Je suis en train de mettre à jour la question principale maintenant. – Charles

+0

@Charles: Eh bien, vous pourriez utiliser '/' et '(?:/| $)' Au lieu de '^' et '$'. – Gumbo

0

Pas besoin d'être si compliqué que ça.

>>> s="abc_def_ghi_jkl_xyz" 
>>> s.rsplit("_",1) 
>>> splitted=s.split("_") 
>>> first=splitted[0] 
>>> last=splitted[-1] 
>>> middle=splitted[1:-1] 
>>> middle='_'.join(splitted[1:-1]) 
>>> print middle 
def_ghi_jkl 
+0

Mon problème est une petite partie d'un problème plus gros, fortement regex. Merci pour votre réponse de toute façon. – Charles

0

Merci pour votre aide à tous! Les deux clés de mon problème où: - ajouter une ancre à la fin de mon modèle - ce qui rend le groupe du milieu non gourmand.

Alors:

/start/of/the/path/(?P<a>[a-z]+)_(?P<b>\w+?)(_(?P<c>[a-z]+))?(/|$) 

De cette façon, toutes les chaînes suivantes sont adaptées:

/jobs/ads/abc_J123/previs/m_name 
/jobs/ads/abc_J123/previs/m_name/ 
/jobs/ads/abc_J123/previs/m_name/some_stuff 
/jobs/ads/abc_J123/previs/m_name/some_stuff/ 
/jobs/ads/abc_J123/previs/m_name/some_stuff/other_stuff 
/jobs/ads/abc_J123/previs/m_name/some_stuff/other_stuff/ 
/jobs/ads/abc_J123/previs/m_name_stage 
/jobs/ads/abc_J123/previs/m_name_stage/ 
/jobs/ads/abc_J123/previs/m_name_stage/some_stuff 
/jobs/ads/abc_J123/previs/m_name_stage/some_stuff/ 
/jobs/ads/abc_J123/previs/m_name_stage/some_stuff/other_stuff 
/jobs/ads/abc_J123/previs/m_name_stage/some_stuff/other_stuff/ 
/jobs/ads/abc_J123/previs/m_long_name_stage 
/jobs/ads/abc_J123/previs/m_long_name_stage/ 
/jobs/ads/abc_J123/previs/m_long_name_stage/some_stuff 
/jobs/ads/abc_J123/previs/m_long_name_stage/some_stuff/ 
/jobs/ads/abc_J123/previs/m_long_name_stage/some_stuff/other_stuff 
/jobs/ads/abc_J123/previs/m_long_name_stage/some_stuff/other_stuff/ 

Merci beaucoup pour votre aide!

Questions connexes