Разделение строк в требуемом формате, Pythonic? (с регулярным выражением или без него)

У меня есть строка в формате:

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

Я хочу получить это:

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

Я сделал это:

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

Это работает по большей части, но не работает, когда в текстовой части есть «@». Например, когда:

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

это терпит неудачу. @names находятся в начале, а после @names может быть текст, который может содержать @.

Ясно, что я могу сначала добавить пробел и узнать первое слово без «@». Но это не кажется элегантным решением.

Каков питонический способ решения этой проблемы?


person lprsd    schedule 17.02.2009    source источник


Ответы (7)


Беззастенчиво опираясь на усилия 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

печатает:

['abc', 'def', 'xyz']
Здравствуйте, это текст, и мой адрес электронной почты: [email protected]


Справедливо призванный к ответу hasen j, позвольте мне пояснить, как это работает:

/@\w+ +/

соответствует одному тегу - @, за которым следует хотя бы один буквенно-цифровой символ, или _, за которым следует хотя бы один символ пробела. + является жадным, поэтому, если есть более одного пробела, он захватит их все.

Чтобы сопоставить любое количество этих тегов, нам нужно добавить плюс (один или несколько элементов) к шаблону для тега; поэтому нам нужно сгруппировать его со скобками:

/(@\w+ +)+/

который соответствует одному или нескольким тегам и, будучи жадным, соответствует всем им. Однако эти скобки теперь возятся с нашими группами захвата, поэтому мы отменяем это, превращая их в анонимную группу:

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

Наконец, мы превращаем это в группу захвата и добавляем еще одну, чтобы убрать все остальное:

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

Последняя разбивка, чтобы подвести итог:

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

Обратите внимание, что при просмотре я улучшил его — \w не нужно было в наборе, и теперь он позволяет использовать несколько пробелов между тегами. Спасибо, hasen-j!

person Brent.Longborough    schedule 17.02.2009
comment
спасибо за расширение :-) Сначала мне было непонятно, что это может быть любое количество слов. Но у меня также были проблемы с поиском правильного синтаксиса для регулярного выражения при повторной попытке. Так я вижу, что анонимная группа теперь внутри, у меня она была снаружи. - person MrTopf; 18.02.2009
comment
не могли бы вы объяснить регулярное выражение? почему он находит переменное количество тегов или что-то еще, что называется @thing? - person hasen; 18.02.2009
comment
Хорошо сыгранный сэр. Спасибо за подробное объяснение. - person mechanical_meat; 22.02.2009
comment
+1 за жирное, подробное объяснение регулярного выражения. Супер полезно для нубов. - person BenjaminGolder; 21.05.2011

Как насчет этого:

  1. Разделение по пространству.
  2. для каждого слова, проверить

    2.1. если слово начинается с @, то нажать на первый список

    2.2. в противном случае просто соедините оставшиеся слова пробелами.

person Osama Al-Maadeed    schedule 17.02.2009

Вы также можете использовать регулярные выражения:

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

Но все зависит от того, как могут выглядеть ваши данные. Так что, возможно, вам придется его отрегулировать. Что он делает, так это создает группу через () и проверяет, что в них разрешено.

person MrTopf    schedule 17.02.2009
comment
OP говорит, что количество @names является переменным - person SilentGhost; 17.02.2009

[изменить: здесь реализовано то, что было предложено Усамой выше]

Это создаст L на основе переменных @ с начала строки, а затем, как только будет найдена не @ var, просто захватите остальную часть строки.

t = '@one @two @three some text   afterward with @ symbols@ meow@meow'

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

Вы можете реорганизовать это, чтобы уменьшить количество кода, но я пытаюсь сделать то, что происходит, очевидным.

person Jason Coon    schedule 17.02.2009

Вот еще один вариант, в котором используется split() и нет регулярных выражений:

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

Вот более короткая, но, возможно, немного загадочная версия, которая использует регулярное выражение для поиска первого пробела, за которым следует символ, отличный от @:

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

Это не работает должным образом, если нет тегов или текста. Формат не указан. Вам нужно будет предоставить больше тестовых случаев для проверки.

person Martin Vilcans    schedule 18.02.2009

person    schedule
comment
Это удалит лишние пробелы между словами, так что это может быть нежелательным побочным эффектом. - person Denilson Sá Maia; 05.11.2010

person    schedule
comment
Начальные @elements могут быть любыми. это не работает - person lprsd; 17.02.2009
comment
это не было указано в вашем первоначальном вопросе, но вот. - person SilentGhost; 17.02.2009