2009-02-17 16 views
6

Tôi có một chuỗi trong các định dạng:Tách chuỗi theo định dạng bắt buộc, cách Pythonic? (Có hoặc w/o Regex)

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

Tôi muốn có được điều này:

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

Tôi đã làm điều này:

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

Nó làm việc cho hầu hết các phần, nhưng nó không thành công khi phần văn bản có '@'. Ví dụ: khi:

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

không thành công. Các tên @ có trong đầu và có thể có văn bản sau @names, có thể chứa @.

Rõ ràng tôi có thể nối thêm với không gian và tìm ra từ đầu tiên mà không có '@'. Nhưng đó không phải là một giải pháp thanh lịch.

Cách giải quyết vấn đề này là gì?

Trả lời

13

Xây dựng không xấu hổ về nỗ lực MrTopf của:

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 

in:

[ 'abc', 'def', 'xyz']
Xin chào phần này là văn bản và email của tôi là foo @ba.r


minh gọi là để giải thích bởi hasen j, hãy để tôi làm rõ cách thức hoạt động này:

/@\w+ +/ 

phù hợp với một thẻ duy nhất - @ tiếp theo ít nhất một chữ số hoặc _ tiếp theo ít nhất một ký tự không gian. + là tham lam, vì vậy nếu có nhiều hơn một không gian, nó sẽ lấy tất cả.

Để khớp với bất kỳ số nào trong số các thẻ này, chúng tôi cần thêm dấu cộng (một hoặc nhiều thứ) vào mẫu cho thẻ; vì vậy chúng tôi cần nhóm nó với dấu ngoặc đơn:

/(@\w+ +)+/ 

khớp với một hoặc nhiều thẻ và tham lam, phù hợp với tất cả chúng. Tuy nhiên, những dấu ngoặc tại fiddle xung quanh với các nhóm chụp của chúng tôi, vì vậy chúng tôi lùi lại rằng bằng cách làm cho họ thành một nhóm nặc danh:

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

Cuối cùng, chúng tôi làm điều đó vào một nhóm chụp và thêm một để quét lên phần còn lại:

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

Một sự cố cuối cùng để tổng hợp:

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

Lưu ý rằng trong Revie cánh này, tôi đã cải thiện nó - \ w không cần phải trong một bộ, và nó bây giờ cho phép nhiều không gian giữa các thẻ. Cảm ơn, hasen-j!

+0

cảm ơn bạn đã mở rộng nó :-) Ban đầu tôi không rõ ràng rằng nó có thể là bất kỳ số lượng từ nào. Nhưng tôi cũng gặp khó khăn khi tìm cú pháp đúng cho regexp khi thử lại. Vì vậy, tôi thấy rằng nhóm vô danh hiện đang ở bên trong, tôi đã có nó bên ngoài. – MrTopf

+0

bạn có muốn giải thích về regex không? tại sao nó tìm thấy số lượng thay đổi của "thẻ" hoặc bất cứ điều gì @ được gọi là gì? – hasen

+1

Sir được chơi tốt. Cảm ơn bạn đã giải thích kỹ lưỡng. – 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

@elements ban đầu có thể là bất kỳ số nào. Điều này không làm việc –

+0

mà không được chỉ định trong câu hỏi ban đầu của bạn, nhưng ở đây bạn đi. – SilentGhost

3

Bạn cũng có thể sử dụng biểu thức thông thường:

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

Nhưng điều này tất cả phụ thuộc vào cách dữ liệu của bạn có thể trông như thế nào. Vì vậy, bạn có thể cần phải điều chỉnh nó. Những gì nó làm là cơ bản tạo nhóm thông qua() và kiểm tra những gì được cho phép trong chúng.

+0

OP cho biết số lượng @names là biến – SilentGhost

5

Làm thế nào về điều này:

  1. Splitting bởi không gian.
  2. từ foreach, kiểm tra

    2.1. nếu từ bắt đầu bằng @ thì hãy đẩy tới danh sách đầu tiên

    2.2. nếu không chỉ cần tham gia các từ còn lại bằng dấu cách.

3

[chỉnh sửa: đây là thực hiện những gì đã được đề xuất bởi Osama trên]

này sẽ tạo ra L dựa trên các biến @ từ đầu của chuỗi, và sau đó một lần một tổ chức phi @ var là tìm thấy, chỉ cần lấy phần còn lại của chuỗi.

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 

Bạn có thể cấu trúc lại mã này ít mã hơn, nhưng tôi đang cố gắng thực hiện những gì đang diễn ra rõ ràng.

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

Tôi thích giải pháp này tốt hơn rồi! bình chọn lên –

+0

Nó sẽ loại bỏ khoảng cách thêm giữa các từ, do đó, điều này có thể không phải là một tác dụng phụ mong muốn. –

1

Dưới đây là chỉ là một biến thể sử dụng split() và không có 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 

Dưới đây là một ngắn hơn nhưng có lẽ một phiên bản hơi khó hiểu rằng sử dụng một regexp để tìm không gian đầu tiên tiếp theo là một phi @ character:

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 

Điều này không hoạt động đúng nếu không có thẻ hoặc không có văn bản. Định dạng không được xác định. Bạn sẽ cần cung cấp thêm các trường hợp kiểm tra để xác thực.

Các vấn đề liên quan