2009-02-17 7 views
6

J'ai une chaîne dans le format suivant:Séparation de chaînes dans le format requis, façon Pythonic? (Avec ou w/o Regex)

t='@abc @def Hello this part is text' 

Je veux obtenir ceci:

l=["abc", "def"] 
s='Hello this part is text' 

Je l'ai fait:

a=t[t.find(' ',t.rfind('@')):].strip() 
s=t[:t.find(' ',t.rfind('@'))].strip() 
b=a.split('@') 
l=[i.strip() for i in b][1:] 

Il fonctionne pour la plupart, mais il échoue lorsque la partie de texte a le '@'. Par exemple, lorsque:

t='@abc @def My email is [email protected]' 

échoue. Les @names sont là au début et il peut y avoir du texte après @names, qui peut éventuellement contenir @.

Il est clair que je peux ajouter initialement un espace et trouver le premier mot sans '@'. Mais cela ne semble pas une solution élégante.

Qu'est-ce qu'une façon pythonique de résoudre ce problème?

Répondre

13

bâtiment sans vergogne sur l'effort de MrTopf:

import re 
rx = re.compile("((?:@\w+ +)+)(.*)") 
t='@abc @def @xyz Hello this part is text and my email is [email protected]' 
a,s = rx.match(t).groups() 
l = re.split('[@ ]+',a)[1:-1] 
print l 
print s 

impressions:

[ 'abc', 'def', 'xyz']
Bonjour cette partie est du texte et mon email est foo @ba.r


justement appelé à rendre des comptes hasen j, permettez-moi de préciser comment cela fonctionne:

/@\w+ +/ 

correspond à une seule étiquette - @ suivi d'au moins un alphanumérique ou _ suivi d'au moins un caractère de l'espace. + est gourmand, donc s'il y a plus d'un espace, il les attrape tous.

Pour correspondre à n'importe quel nombre de ces étiquettes, nous devons ajouter un plus (une ou plusieurs choses) au modèle pour le tag; donc nous devons groupe avec parenthèses:

/(@\w+ +)+/ 

qui correspond à l'une ou plusieurs étiquettes-et, étant avide, correspond à tous. Cependant, ces parenthèses tripoter maintenant autour de nos groupes de capture, donc nous défont qu'en les faisant dans un groupe anonyme:

/(?:@\w+ +)+/ 

Enfin, nous faisons cela dans un groupe de capture et ajouter un autre pour balayer le reste:

/((?:@\w+ +)+)(.*)/ 

Une dernière rupture pour résumer:

((?:@\w+ +)+)(.*) 
(?:@\w+ +)+ 
( @\w+ +) 
    @\w+ + 

Notez que dans Revie aile cela, je l'ai amélioré - \ w n'a pas besoin d'être dans un ensemble, et il permet maintenant plusieurs espaces entre les balises. Merci, hasen-j!

+0

merci de l'étendre :-) Il ne m'était pas clair au début que cela peut être n'importe quel nombre de mots. Mais j'ai aussi eu des problèmes pour trouver la bonne syntaxe pour l'expression rationnelle lorsque j'essayais de nouveau. Donc, je vois que le groupe anonyme est maintenant à l'intérieur, je l'ai eu à l'extérieur. – MrTopf

+0

auriez-vous la peine d'expliquer la regex? pourquoi trouve-t-il un nombre variable de "tags" ou quoi que ce soit @thing est appelé? – hasen

+1

Bien joué Sir. Merci pour l'explication approfondie. – bernie

3
[i.strip('@') for i in t.split(' ', 2)[:2]]  # for a fixed number of @def 
a = [i.strip('@') for i in t.split(' ') if i.startswith('@')] 
s = ' '.join(i for i in t.split(' ') if not i.startwith('@')) 
+0

Les @éléments initiaux peuvent être en nombre. Cela ne fonctionne pas –

+0

qui n'a pas été spécifié dans votre question initiale, mais c'est parti. – SilentGhost

3

Vous pouvez également utiliser des expressions régulières:

import re 
rx = re.compile("@([\w]+) @([\w]+) (.*)") 
t='@abc @def Hello this part is text and my email is [email protected]' 
a,b,s = rx.match(t).groups() 

Mais tout cela dépend de la façon dont vos données peuvent ressembler. Donc vous devrez peut-être l'ajuster. Ce qu'il fait est en train de créer un groupe via() et de vérifier ce qui est autorisé en eux.

+0

OP dit que le nombre de @names est variable – SilentGhost

5

Que diriez-vous ceci:

  1. Fractionnement par l'espace.
  2. pour chaque mot, vérifier

    2.1. si le mot commence par @ alors Pousser vers la première liste

    2.2. sinon, il suffit de joindre les mots restants par des espaces.

3

[modifier: ce met en œuvre ce qui est suggéré par Oussama ci-dessus]

Cela va créer L en fonction des variables @ depuis le début de la chaîne, puis une fois par var non @ est trouvé, il suffit de saisir le reste de la chaîne.

t = '@one @two @three some text afterward with @ [email protected] [email protected]' 

words = t.split(' ')   # split into list of words based on spaces 
L = [] 
s = '' 
for i in range(len(words)): # go through each word 
    word = words[i] 
    if word[0] == '@':  # grab @'s from beginning of string 
     L.append(word[1:]) 
     continue 
    s = ' '.join(words[i:]) # put spaces back in 
    break     # you can ignore the rest of the words 

Vous pouvez refactoriser ceci pour être moins de code, mais j'essaye de rendre évident ce qui se passe.

7
t='@abc @def Hello this part is text' 

words = t.split(' ') 

names = [] 
while words: 
    w = words.pop(0) 
    if w.startswith('@'): 
     names.append(w[1:]) 
    else: 
     break 

text = ' '.join(words) 

print names 
print text 
+0

J'aime mieux cette solution que la mienne! voté –

+0

Il enlèvera espacement supplémentaire entre les mots, donc ce ne pourrait pas être un effet secondaire souhaité. –

1

est ici juste une autre variante qui utilise split() et pas regexpes:

t='@abc @def My email is [email protected]' 
tags = [] 
words = iter(t.split()) 

# iterate over words until first non-tag word 
for w in words: 
    if not w.startswith("@"): 
    # join this word and all the following 
    s = w + " " + (" ".join(words)) 
    break 
    tags.append(w[1:]) 
else: 
    s = "" # handle string with only tags 

print tags, s 

est ici plus courte, mais peut-être une version un peu cryptique qui utilise une expression rationnelle pour trouver le premier espace suivi d'un non @ caractère:

import re 
t = '@abc @def My email is [email protected] @extra bye' 
m = re.search(r"\s([^@].*)$", t) 
tags = [tag[1:] for tag in t[:m.start()].split()] 
s = m.group(1) 
print tags, s # ['abc', 'def'] My email is [email protected] @extra bye 

Ceci ne fonctionne pas correctement s'il n'y a pas de balise ou pas de texte. Le format est sous-spécifié. Vous devrez fournir plus de cas de test à valider.

Questions connexes