2009-02-17 8 views
6

Mam ciąg w formacie:Rozdzielanie łańcuchów w wymaganym formacie, sposób Pythonic? (Z lub w/o Regex)

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

chcę uzyskać to:

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

Zrobiłem to:

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:] 

It działa w większości przypadków, ale kończy się niepowodzeniem, gdy część tekstowa ma znak "@". Np. Gdy:

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

nie powiedzie się. Nazwiska @ są tam na początku i może być tekst po imionach, które mogą zawierać @.

Najwyraźniej mogę dołączyć początkowo spację i znaleźć pierwsze słowo bez "@". Ale to nie wydaje się być eleganckim rozwiązaniem.

Co to jest pytonowa metoda rozwiązania tego problemu?

Odpowiedz

13

budynku bezwstydnie na MrTopf za wysiłek:

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 

nadrukami:

[ 'abc', 'DEF', 'xyz']
Witam ta część tekstu, a mój email jest foo @ba.r


Słusznie pociągnięty do odpowiedzialności przez hasen j, pozwól mi wyjaśnić, jak to działa:

/@\w+ +/ 

dopasowuje pojedynczy tag - @ następnie przez co najmniej jedną alfanumerycznych lub _ a następnie co najmniej jeden spacja. + jest chciwy, więc jeśli jest więcej niż jedno miejsce, to złapie je wszystkie.

Aby dopasować dowolną liczbę tych tagów, musimy dodać znak plus (jedną lub więcej rzeczy) do wzorca dla tagu; więc musimy grupy niego z nawiasami:

/(@\w+ +)+/ 

który pasuje do jednego-or-więcej znaczników, a będąc zachłanny, pasuje do wszystkich z nich. Jednakże te nawiasy teraz bawić się wokół z naszych grup przechwytujących, więc cofnąć że dokonując ich do anonimowej grupy:

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

Wreszcie, sprawi że w grupie przechwytywania i dodać kolejny zamiatać resztę:

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

ostatnim podział Podsumowując:

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

pamiętać, że w Revie Rozwiń to, poprawiłem - \ w nie musiał być w zestawie i teraz pozwala na wiele spacji między tagami. Dzięki, hasen-j!

+0

dzięki za przedłużenie :-) Początkowo nie było dla mnie jasne, że może to być dowolna liczba słów. Ale miałem również problem ze znalezieniem właściwej składni dla wyrażeń regularnych, gdy próbowałem ponownie. Widzę więc, że anonimowa grupa jest teraz w środku, miałem ją na zewnątrz. – MrTopf

+0

Czy chciałbyś wyjaśnić wyrażenie regularne? dlaczego znajduje zmienną liczbę "tagów" lub cokolwiek cokolwiek nazywa? – hasen

+1

Dobrze grał Sir. Dzięki za dokładne wyjaśnienie. – 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

Początkowe @elements może być dowolną liczbą. To nie działa –

+0

, który nie został określony w oryginalnym pytaniu, ale proszę bardzo. – SilentGhost

3

Można również użyć wyrażeń regularnych:

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() 

Ale to wszystko zależy od tego w jaki sposób dane mogą wyglądać. Być może będziesz musiał go dostosować. To, co robi, to zasadniczo tworzenie grupy via() i sprawdzanie, co w nich jest dozwolone.

+0

OP mówi, że liczba @names jest zmienna – SilentGhost

5

Jak o tym:

  1. Dzielenie przez przestrzeń.
  2. foreach słowo, sprawdź

    2.1. jeśli słowo zaczyna się od @, a następnie naciśnij na pierwszą listę

    2.2. w przeciwnym razie po prostu dołącz pozostałe słowa spacjami.

3

[edit: ten realizuje co zostało zasugerowane przez Osama powyżej]

Stworzy L na podstawie zmiennych @ od początku napisu, a następnie raz w non @ var jest znaleźć, po prostu złap resztę struny.

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 

Możesz zmienić to, aby mniej kodu, ale próbuję zrobić to, co oczywiste.

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

Podoba mi się to rozwiązanie lepiej niż moje! głosował w górę –

+0

Usunie dodatkowy odstęp między słowami, więc może to nie być pożądanym efektem ubocznym. –

1

Oto tylko kolejna wariacja, która używa split() i nie ma 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 

Oto krótsze, ale może nieco tajemnicze wersja, która używa wyrażenia regularnego do znalezienia pierwszego miejsca po którym następuje nie- znak @:

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 

To nie działa poprawnie, jeśli nie ma tagów ani tekstu. Format jest niedookreślony. Musisz podać więcej przypadków testowych do sprawdzenia poprawności.