String separation in required format, Pythonic way? (with or w/o Regex)

北城余情 提交于 2020-01-01 05:15:10

问题


I have a string in the format:

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

I want to get this:

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

I did this:

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 works for the most part, but it fails when the text part has the '@'. Eg, when:

t='@abc @def My email is red@hjk.com'

it fails. The @names are there in the beginning and there can be text after @names, which may possibly contain @.

Clearly I can append initally with a space and find out first word without '@'. But that doesn't seem an elegant solution.

What is a pythonic way of solving this?


回答1:


Building unashamedly on MrTopf's effort:

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

prints:

['abc', 'def', 'xyz']
Hello this part is text and my email is foo@ba.r


Justly called to account by hasen j, let me clarify how this works:

/@\w+ +/

matches a single tag - @ followed by at least one alphanumeric or _ followed by at least one space character. + is greedy, so if there is more than one space, it will grab them all.

To match any number of these tags, we need to add a plus (one or more things) to the pattern for tag; so we need to group it with parentheses:

/(@\w+ +)+/

which matches one-or-more tags, and, being greedy, matches all of them. However, those parentheses now fiddle around with our capture groups, so we undo that by making them into an anonymous group:

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

Finally, we make that into a capture group and add another to sweep up the rest:

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

A last breakdown to sum up:

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

Note that in reviewing this, I've improved it - \w didn't need to be in a set, and it now allows for multiple spaces between tags. Thanks, hasen-j!




回答2:


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



回答3:


How about this:

  1. Splitting by space.
  2. foreach word, check

    2.1. if word starts with @ then Push to first list

    2.2. otherwise just join the remaining words by spaces.




回答4:


You might also use regular expressions:

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

But this all depends on how your data can look like. So you might need to adjust it. What it does is basically creating group via () and checking for what's allowed in them.




回答5:


 [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('@'))



回答6:


[edit: this is implementing what was suggested by Osama above]

This will create L based on the @ variables from the beginning of the string, and then once a non @ var is found, just grab the rest of the string.

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

You can refactor this to be less code, but I'm trying to make what is going on obvious.




回答7:


Here's just another variation that uses split() and no regexpes:

t='@abc @def My email is red@hjk.com'
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

Here's a shorter but perhaps a bit cryptic version that uses a regexp to find the first space followed by a non-@ character:

import re
t = '@abc @def My email is red@hjk.com @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 red@hjk.com @extra bye

This doesn't work properly if there are no tags or no text. The format is underspecified. You'll need to provide more test cases to validate.



来源:https://stackoverflow.com/questions/558105/string-separation-in-required-format-pythonic-way-with-or-w-o-regex

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!