Tách chuỗi thành các từ có nhiều dấu phân cách ranh giới từ


670

Tôi nghĩ rằng những gì tôi muốn làm là một nhiệm vụ khá phổ biến nhưng tôi không tìm thấy tài liệu tham khảo nào trên web. Tôi có văn bản với dấu câu, và tôi muốn một danh sách các từ.

"Hey, you - what are you doing here!?"

nên là

['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Nhưng Python str.split()chỉ hoạt động với một đối số, vì vậy tôi có tất cả các từ có dấu chấm câu sau khi tôi phân tách bằng khoảng trắng. Có ý kiến ​​gì không?



6
python's str.split()cũng hoạt động mà không có tranh luận gì cả
Ivan Vinogradov

Câu trả lời:


467

Một trường hợp trong đó các biểu thức chính quy là hợp lý:

import re
DATA = "Hey, you - what are you doing here!?"
print re.findall(r"[\w']+", DATA)
# Prints ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

2
Cảm ơn. Mặc dù vậy, vẫn quan tâm - làm thế nào tôi có thể thực hiện thuật toán được sử dụng trong mô-đun này? Và tại sao nó không xuất hiện trong mô-đun chuỗi?
ooboo

29
Biểu hiện thông thường có thể gây nản lòng lúc đầu, nhưng rất mạnh mẽ. Biểu thức chính quy '\ w +' có nghĩa là "một ký tự từ (az, v.v.) được lặp lại một hoặc nhiều lần". Có một HOWTO trên Python biểu thức thông thường ở đây: amk.ca/python/howto/regex
RichieHindle

324
Đây không phải là câu trả lời cho câu hỏi. Đây là một câu trả lời cho một câu hỏi khác, xảy ra để làm việc cho tình huống cụ thể này. Như thể có ai đó hỏi "làm thế nào để tôi rẽ trái" và câu trả lời được bình chọn hàng đầu là "thực hiện ba lượt rẽ tiếp theo". Nó hoạt động cho một số giao lộ nhất định, nhưng nó không đưa ra câu trả lời cần thiết. Trớ trêu thay, câu trả lời trong re, chỉ là không findall. Câu trả lời dưới đây re.split()là vượt trội.
Jesse Dhillon

4
@JesseDhillon "lấy tất cả các chuỗi con bao gồm một chuỗi các ký tự từ" và "phân chia trên tất cả các chuỗi bao gồm một chuỗi các ký tự không từ" theo nghĩa đen chỉ là các cách khác nhau để diễn đạt cùng một hoạt động; Tôi không chắc tại sao bạn gọi một trong hai câu trả lời vượt trội.
Đánh dấu Amery

4
@TMWP: Apostophe có nghĩa là một từ giống như don'tđược coi là một từ duy nhất, thay vì được chia thành dont.
RichieHulum

574

re.split ()

re.split (mẫu, chuỗi [, maxsplit = 0])

Chia chuỗi bởi sự xuất hiện của mẫu. Nếu bắt giữ dấu ngoặc đơn được sử dụng trong mẫu, thì văn bản của tất cả các nhóm trong mẫu cũng được trả về như một phần của danh sách kết quả. Nếu maxsplit là khác không, thì hầu hết các phần tách maxsplit xảy ra và phần còn lại của chuỗi được trả về là phần tử cuối cùng của danh sách. (Lưu ý không tương thích: trong bản phát hành Python 1.5 ban đầu, maxsplit đã bị bỏ qua. Điều này đã được sửa trong các bản phát hành sau.)

>>> re.split('\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split('(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split('\W+', 'Words, words, words.', 1)
['Words', 'words, words.']

13
Giải pháp này có ưu điểm là dễ dàng thích nghi để phân chia trên các phần dưới, điều mà giải pháp findall không có: in re.split ("\ W + | _", "Kiểm tra this_thing") 'mang lại: [' Kiểm tra ',' này ' , 'điều']
Emil Stenström

63
Bây giờ nếu duy nhất tôi có thể nhớ sự khác biệt giữa \w, \W, \s, và \S. Bất cứ ai nghĩ rằng viết hoa của một lá cờ nên đảo ngược ý nghĩa của nó cần phải được bắn qua đầu.
ArtOfWarfare

1
Một trường hợp sử dụng phổ biến của việc tách chuỗi là loại bỏ các mục chuỗi rỗng khỏi kết quả cuối cùng. Có thể làm điều đó với phương pháp này? re.split ('\ W +', 'abc') dẫn đến ['', 'a', 'b', 'c', '']
Scott Morken

3
@ArtOfWarfare Người ta thường sử dụng shiftkhóa để làm ngược lại với một cái gì đó. ctrl+zhoàn tác so ctrl+shift+zvới làm lại. Vì vậy shift w, hoặc W, sẽ ngược lại w.
Frank Vel

1
Câu trả lời này phải ở trên cùng - đó là câu duy nhất trả lời chính xác tiêu đề câu hỏi.
Kranach

380

Một cách nhanh chóng khác để thực hiện việc này mà không cần regrec là thay thế các ký tự trước, như sau:

>>> 'a;bcd,ef g'.replace(';',' ').replace(',',' ').split()
['a', 'bcd', 'ef', 'g']

71
Nhanh chóng và bẩn thỉu nhưng hoàn hảo cho trường hợp của tôi (dải phân cách của tôi là một bộ nhỏ, được biết đến)
Andy Baker

7
Hoàn hảo cho trường hợp bạn không có quyền truy cập vào thư viện RE, chẳng hạn như một số bộ vi điều khiển nhỏ. :-)
tu-Tái lập Monica-dor duh

11
Tôi nghĩ điều này cũng rõ ràng hơn RE, vì vậy nó rất thân thiện. Đôi khi không cần giải pháp chung cho mọi thứ
Adam Hughes

Tuyệt vời. Tôi đã có một .split () trong nhiều tình huống đầu vào và cần phải nắm bắt khi người dùng, tôi, tách các đầu vào bằng một khoảng trắng và không phải là dấu phẩy. Tôi đã định bỏ cuộc và làm lại với re, nhưng giải pháp .replace () của bạn đập vào đầu đinh. Cảm ơn.
JayJay123

nó sẽ khiến bạn trả lời sai khi bạn không muốn phân chia trên các khoảng trắng và bạn muốn phân chia các ký tự khác.
Ahmed Amr

307

Vì vậy, rất nhiều câu trả lời, nhưng tôi không thể tìm thấy bất kỳ giải pháp nào thực hiện hiệu quả tiêu đề của các câu hỏi theo nghĩa đen (chia tách trên nhiều dấu phân cách có thể thay vào đó, nhiều câu trả lời phân tách trên bất kỳ từ nào không phải là một từ khác nhau). Vì vậy, đây là một câu trả lời cho câu hỏi trong tiêu đề, dựa trên remô-đun chuẩn và hiệu quả của Python :

>>> import re  # Will be splitting on: , <space> - ! ? :
>>> filter(None, re.split("[, \-!?:]+", "Hey, you - what are you doing here!?"))
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Ở đâu:

  • các […]trận đấu một trong những dải phân cách được liệt kê bên trong,
  • các \-trong biểu thức chính quy là ở đây để ngăn chặn việc giải thích đặc biệt của -như một chỉ số phạm vi nhân vật (như trong A-Z),
  • các +bỏ qua một hoặc nhiều delimiters (nó có thể được bỏ qua nhờ sự filter(), nhưng điều này không cần thiết sẽ tạo ra chuỗi rỗng giữa dải phân cách đối sánh) và
  • filter(None, …) loại bỏ các chuỗi trống có thể được tạo bởi các dấu tách hàng đầu và dấu (vì các chuỗi trống có giá trị boolean sai).

Điều này re.split()chính xác "chia tách với nhiều dấu phân cách", như được yêu cầu trong tiêu đề câu hỏi.

Giải pháp này còn miễn nhiễm với các vấn đề với các ký tự không phải ASCII trong các từ được tìm thấy trong một số giải pháp khác (xem bình luận đầu tiên cho câu trả lời của ghostdog74 ).

Các remô-đun có nhiều hiệu quả hơn (về tốc độ và tính súc tích) so với làm Python vòng lặp và kiểm tra "bằng tay"!


3
"Tôi không thể tìm thấy bất kỳ giải pháp nào thực hiện hiệu quả tiêu đề của các câu hỏi theo nghĩa đen" - câu trả lời thứ hai thực hiện điều đó, được đăng 5 năm trước: stackoverflow.com/a/1059601/2642204 .
BartoszKP

17
Câu trả lời này không phân chia tại các dấu phân cách (từ một tập hợp nhiều dấu phân cách): thay vào đó, nó phân tách ở bất cứ thứ gì không phải là chữ và số. Điều đó nói rằng, tôi đồng ý rằng ý định của người đăng ban đầu có lẽ chỉ giữ lại các từ, thay vì xóa một số dấu chấm câu.
Eric O Lebigot

EOL: Tôi nghĩ rằng câu trả lời này không phân chia trên một tập hợp nhiều dấu chấm. Nếu bạn thêm không chữ và số vào chuỗi không được chỉ định, như gạch dưới, chúng sẽ không bị phân tách, như mong đợi.
GravityWell

@GravityWell: Tôi không chắc là tôi hiểu: bạn có thể đưa ra một ví dụ cụ thể không?
Eric O Lebigot

3
@EOL: Tôi chỉ nhận ra rằng tôi đã bối rối bởi nhận xét của bạn "Câu trả lời này không phân tách ..." Tôi nghĩ "này" đề cập đến câu trả lời re.split của bạn, nhưng bây giờ tôi nhận ra bạn có nghĩa là câu trả lời của gimel. Tôi nghĩ câu trả lời NÀY (câu trả lời mà tôi đang bình luận) là câu trả lời hay nhất :)
GravityWell 8/12/14

56

Một cách khác, không có regex

import string
punc = string.punctuation
thestring = "Hey, you - what are you doing here!?"
s = list(thestring)
''.join([o for o in s if not o in punc]).split()

8
Giải pháp này thực sự tốt hơn giải pháp được chấp nhận. Nó hoạt động mà không có ký tự ASCII, hãy thử "Hey, you - what are you doing here María!?". Giải pháp được chấp nhận sẽ không hoạt động với ví dụ trước.
Christopher Ramírez

4
Tôi nghĩ có một vấn đề nhỏ ở đây ... Mã của bạn sẽ thêm ký tự được tách ra với dấu chấm câu và do đó sẽ không chia cho họ ... Nếu tôi không sai, dòng cuối cùng của bạn nên:''.join([o if not o in string.punctuation else ' ' for o in s]).split()
cedbeu

Thư viện biểu thức chính quy có thể được tạo để chấp nhận các quy ước Unicode cho các ký tự nếu cần. Ngoài ra, điều này có cùng một vấn đề mà giải pháp được chấp nhận đã từng có: như bây giờ, nó phân tách trên dấu nháy đơn. Bạn có thể muốn o for o in s if (o in not string.punctuation or o == "'"), nhưng sau đó nó trở nên quá phức tạp đối với một lớp lót nếu chúng ta cũng thêm vào bản vá của cedbeu.
Daniel H

Có một vấn đề khác ở đây. Ngay cả khi chúng tôi tính đến các thay đổi của @cedbeu, mã này không hoạt động nếu chuỗi giống như thế "First Name,Last Name,Street Address,City,State,Zip Code"và chúng tôi chỉ muốn phân tách bằng dấu phẩy ,. Đầu ra mong muốn sẽ là: ['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']Những gì chúng ta nhận được thay vào đó:['First', 'Name', 'Last', 'Name', 'Street', 'Address', 'City', 'State', 'Zip', 'Code']
Stefan van den Akker

4
Giải pháp này cực kỳ kém hiệu quả: đầu tiên danh sách được giải mã thành các ký tự riêng lẻ, sau đó toàn bộ các ký tự dấu chấm câu được chuyển qua cho mỗi ký tự đơn trong chuỗi gốc, sau đó các ký tự được ghép lại, sau đó phân tách lại. Tất cả "chuyển động" này cũng rất phức tạp, so với một giải pháp dựa trên biểu thức thông thường: ngay cả khi tốc độ không quan trọng trong một ứng dụng nhất định, không cần một giải pháp phức tạp. Vì remô-đun là tiêu chuẩn và cung cấp cả mức độ dễ đọc và tốc độ, tôi không hiểu tại sao nó nên được tránh.
Eric O Lebigot

39

Pro-Tip: Sử dụng string.translatecho các hoạt động chuỗi nhanh nhất mà Python có.

Một số bằng chứng ...

Đầu tiên, cách chậm (xin lỗi pprzemek):

>>> import timeit
>>> S = 'Hey, you - what are you doing here!?'
>>> def my_split(s, seps):
...     res = [s]
...     for sep in seps:
...         s, res = res, []
...         for seq in s:
...             res += seq.split(sep)
...     return res
... 
>>> timeit.Timer('my_split(S, punctuation)', 'from __main__ import S,my_split; from string import punctuation').timeit()
54.65477919578552

Tiếp theo, chúng tôi sử dụng re.findall()(như được đưa ra bởi câu trả lời được đề xuất). Nhanh hơn nhiều:

>>> timeit.Timer('findall(r"\w+", S)', 'from __main__ import S; from re import findall').timeit()
4.194725036621094

Cuối cùng, chúng tôi sử dụng translate:

>>> from string import translate,maketrans,punctuation 
>>> T = maketrans(punctuation, ' '*len(punctuation))
>>> timeit.Timer('translate(S, T).split()', 'from __main__ import S,T,translate').timeit()
1.2835021018981934

Giải trình:

string.translateđược triển khai trong C và không giống như nhiều hàm thao tác chuỗi trong Python, string.translate không tạo ra một chuỗi mới. Vì vậy, nó nhanh như bạn có thể nhận được để thay thế chuỗi.

Tuy nhiên, điều này hơi khó xử vì nó cần một bảng dịch để thực hiện phép thuật này. Bạn có thể tạo một bảng dịch với maketrans()chức năng tiện lợi. Mục tiêu ở đây là dịch tất cả các ký tự không mong muốn sang không gian. Một thay thế một cho một. Một lần nữa, không có dữ liệu mới được sản xuất. Vì vậy, điều này là nhanh chóng !

Tiếp theo, chúng tôi sử dụng tốt cũ split(). split()theo mặc định sẽ hoạt động trên tất cả các ký tự khoảng trắng, nhóm chúng lại với nhau để phân chia. Kết quả sẽ là danh sách các từ mà bạn muốn. Và cách tiếp cận này nhanh hơn gần gấp 4 lần re.findall()!


4
Tôi đã thực hiện một thử nghiệm ở đây và nếu bạn cần sử dụng unicode, sử dụng patt = re.compile(ur'\w+', re.UNICODE); patt.findall(S)nhanh hơn dịch, bởi vì bạn phải mã hóa chuỗi trước khi áp dụng biến đổi và giải mã từng mục trong danh sách sau khi tách để quay lại unicode.
Rafael S. Calsaverini

Bạn có thể thực hiện một lần triển khai dịch và đảm bảo rằng S không nằm trong số các bộ chia với:s.translate(''.join([(chr(i) if chr(i) not in seps else seps[0]) for i in range(256)])).split(seps[0])
hobs

Không có thực hiện. Bạn đang so sánh táo và cam. ;) giải pháp của tôi trong python 3 vẫn hoạt động; P và có hỗ trợ cho các bộ tách đa char. :) hãy thử làm điều đó theo cách đơn giản mà không cần phân bổ một chuỗi mới. :) nhưng đúng, tôi bị giới hạn trong việc phân tích cú pháp params dòng lệnh và không phải là một cuốn sách chẳng hạn.
pprzemek

bạn nói "không tạo ra một chuỗi mới", có nghĩa là nó hoạt động tại chỗ trên chuỗi đã cho? Tôi đã thử nghiệm nó với python 2.7 và nó không sửa đổi chuỗi gốc và trả về chuỗi mới.
Prokop Hapala

26

Tôi đã có một tình huống khó xử tương tự và không muốn sử dụng mô-đun 're'.

def my_split(s, seps):
    res = [s]
    for sep in seps:
        s, res = res, []
        for seq in s:
            res += seq.split(sep)
    return res

print my_split('1111  2222 3333;4444,5555;6666', [' ', ';', ','])
['1111', '', '2222', '3333', '4444', '5555', '6666']

1
Tôi thích điều này. Chỉ cần một lưu ý, thứ tự của dải phân cách có vấn đề. Xin lỗi nếu điều đó là hiển nhiên.
crizCraig

2
Tại sao không sử dụng remô-đun, cả hai cách nhanh hơn và rõ ràng hơn (không phải là các biểu thức thông thường đặc biệt rõ ràng, nhưng vì nó ngắn hơn và trực tiếp)?
Eric O Lebigot

13

Đầu tiên, tôi muốn đồng ý với những người khác rằng các str.translate(...)giải pháp regex hoặc dựa trên là hiệu quả nhất. Đối với trường hợp sử dụng của tôi, hiệu suất của chức năng này không đáng kể, vì vậy tôi muốn thêm ý tưởng mà tôi đã xem xét với tiêu chí đó.

Mục tiêu chính của tôi là khái quát hóa các ý tưởng từ một số câu trả lời khác thành một giải pháp có thể hoạt động cho các chuỗi chứa nhiều hơn chỉ các từ regex (nghĩa là liệt kê các tập hợp con rõ ràng của các ký tự dấu chấm câu so với các ký tự từ trong danh sách trắng).

Lưu ý rằng, trong bất kỳ cách tiếp cận nào, người ta cũng có thể xem xét sử dụng string.punctuationthay cho danh sách được xác định thủ công.

Tùy chọn 1 - re.sub

Tôi đã rất ngạc nhiên khi thấy không có câu trả lời cho đến nay sử dụng re.sub (...) . Tôi thấy đó là một cách tiếp cận đơn giản và tự nhiên cho vấn đề này.

import re

my_str = "Hey, you - what are you doing here!?"

words = re.split(r'\s+', re.sub(r'[,\-!?]', ' ', my_str).strip())

Trong giải pháp này, tôi lồng cuộc gọi vào re.sub(...)bên trong re.split(...)- nhưng nếu hiệu suất là quan trọng, việc biên dịch regex bên ngoài có thể có lợi - đối với trường hợp sử dụng của tôi, sự khác biệt là đáng kể, vì vậy tôi thích sự đơn giản và dễ đọc hơn.

Tùy chọn 2 - str.replace

Đây là một vài dòng nữa, nhưng nó có lợi ích là có thể mở rộng mà không cần phải kiểm tra xem bạn có cần thoát khỏi một nhân vật nào đó trong regex hay không.

my_str = "Hey, you - what are you doing here!?"

replacements = (',', '-', '!', '?')
for r in replacements:
    my_str = my_str.replace(r, ' ')

words = my_str.split()

Thay vào đó, thật tuyệt khi có thể ánh xạ str.replace vào chuỗi, nhưng tôi không nghĩ nó có thể được thực hiện bằng các chuỗi bất biến, và trong khi ánh xạ vào danh sách các ký tự sẽ hoạt động, chạy mọi thay thế đối với mọi ký tự Nghe có vẻ quá đáng. (Chỉnh sửa: Xem tùy chọn tiếp theo để biết ví dụ về chức năng.)

Tùy chọn 3 - funcools.reduce

(Trong Python 2, reducecó sẵn trong không gian tên toàn cầu mà không cần nhập nó từ funcools.)

import functools

my_str = "Hey, you - what are you doing here!?"

replacements = (',', '-', '!', '?')
my_str = functools.reduce(lambda s, sep: s.replace(sep, ' '), replacements, my_str)
words = my_str.split()

Hừm, một phương pháp khác là sử dụng str.translate- nó không có khả năng unicode nhưng có khả năng nhanh hơn các phương thức khác và như vậy có thể tốt trong một số trường hợp: replacements=',-!?'; import string; my_str = my_str.translate(string.maketrans(replacements, ' ' * len(replacements)))Ngoài ra, ở đây bắt buộc phải thay thế dưới dạng một chuỗi ký tự, không phải là tuple hoặc danh sách.
MarSoft

@MarSoft Cảm ơn! Tôi đã đề cập rằng một ở đầu câu trả lời nhưng quyết định không thêm nó vì các câu trả lời hiện có đã thảo luận tốt về nó.
Taylor Edmiston

10
join = lambda x: sum(x,[])  # a.k.a. flatten1([[1],[2,3],[4]]) -> [1,2,3,4]
# ...alternatively...
join = lambda lists: [x for l in lists for x in l]

Sau đó, điều này trở thành một ba lớp:

fragments = [text]
for token in tokens:
    fragments = join(f.split(token) for f in fragments)

Giải trình

Đây là những gì trong Haskell được gọi là danh sách đơn nguyên. Ý tưởng đằng sau đơn nguyên là một khi "trong đơn nguyên" bạn "ở lại đơn nguyên" cho đến khi có thứ gì đó đưa bạn ra ngoài. Ví dụ: trong Haskell, giả sử bạn ánh xạ range(n) -> [1,2,...,n]hàm python qua Danh sách. Nếu kết quả là Danh sách, nó sẽ được thêm vào Danh sách tại chỗ, vì vậy bạn sẽ nhận được một cái gì đó như thế map(range, [3,4,1]) -> [0,1,2,0,1,2,3,0]. Điều này được gọi là bản đồ bổ sung (hoặc mappend, hoặc có thể một cái gì đó tương tự). Ý tưởng ở đây là bạn đã thực hiện thao tác này mà bạn đang áp dụng (chia nhỏ mã thông báo) và bất cứ khi nào bạn thực hiện điều đó, bạn sẽ tham gia kết quả vào danh sách.

Bạn có thể trừu tượng hóa điều này thành một chức năng và có tokens=string.punctuationmặc định.

Ưu điểm của phương pháp này:

  • Cách tiếp cận này (không giống như các cách tiếp cận dựa trên regex ngây thơ) có thể hoạt động với các mã thông báo có độ dài tùy ý (mà regex cũng có thể thực hiện với cú pháp nâng cao hơn).
  • Bạn không bị hạn chế chỉ các mã thông báo; bạn có thể có logic tùy ý thay cho mỗi mã thông báo, ví dụ: một trong số các "mã thông báo" có thể là một hàm phân tách theo cách các dấu ngoặc đơn được lồng vào nhau.

Giải pháp Neat Haskell, nhưng IMO này có thể được viết rõ ràng hơn mà không cần mappend trong Python.
Vlad the Impala

@Goose: điểm quan trọng là chức năng 2 dòng map_then_appendcó thể được sử dụng để biến vấn đề thành 2 lớp, cũng như nhiều vấn đề khác dễ viết hơn nhiều. Hầu hết các giải pháp khác sử dụng remô-đun biểu thức chính quy , không phải là python. Nhưng tôi đã không hài lòng với cách tôi làm cho câu trả lời của mình có vẻ không phù hợp và thú vị khi nó thực sự ngắn gọn ... Tôi sẽ chỉnh sửa nó ...
ninjagecko

cái này được cho là đang hoạt động trong Python như được viết? fragmentskết quả của tôi chỉ là một danh sách các ký tự trong chuỗi (bao gồm các mã thông báo).
Rick hỗ trợ Monica

@RickTeachey: nó hoạt động với tôi trong cả python2 và python3.
ninjagecko

hmmmm Có lẽ ví dụ này hơi mơ hồ. Tôi đã thử mã trong câu trả lời tất cả các loại ways- khác nhau bao gồm có fragments = ['the,string'], fragments = 'the,string'hoặc fragments = list('the,string')và không ai trong số họ đang sản xuất sản lượng đúng.
Rick ủng hộ Monica

5

thử cái này:

import re

phrase = "Hey, you - what are you doing here!?"
matches = re.findall('\w+', phrase)
print matches

cái này sẽ in ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']


4

Sử dụng thay thế hai lần:

a = '11223FROM33344INTO33222FROM3344'
a.replace('FROM', ',,,').replace('INTO', ',,,').split(',,,')

kết quả trong:

['11223', '33344', '33222', '3344']

4

Tôi thích re , nhưng đây là giải pháp của tôi mà không có nó:

from itertools import groupby
sep = ' ,-!?'
s = "Hey, you - what are you doing here!?"
print [''.join(g) for k, g in groupby(s, sep.__contains__) if not k]

sep .__ chứa__ là một phương thức được sử dụng bởi toán tử 'in'. Về cơ bản nó giống như

lambda ch: ch in sep

nhưng ở đây thuận tiện hơn.

groupby được chuỗi và chức năng của chúng tôi. Nó phân tách chuỗi trong các nhóm bằng hàm đó: bất cứ khi nào giá trị của hàm thay đổi - một nhóm mới được tạo. Vì vậy, sep .__ chứa__ chính xác là những gì chúng ta cần.

groupby trả về một chuỗi các cặp, trong đó cặp [0] là kết quả của chức năng của chúng tôi và cặp [1] là một nhóm. Sử dụng 'nếu không k', chúng tôi lọc ra các nhóm có dấu phân cách (vì kết quả của sep .__ chứa__ là Đúng trên dấu phân cách). Chà, đó là tất cả - bây giờ chúng ta có một chuỗi các nhóm trong đó mỗi nhóm là một từ (nhóm thực sự là một lần lặp để chúng ta sử dụng phép nối để chuyển đổi nó thành chuỗi).

Giải pháp này khá chung chung, bởi vì nó sử dụng một hàm để tách chuỗi (bạn có thể phân tách theo bất kỳ điều kiện nào bạn cần). Ngoài ra, nó không tạo các chuỗi / danh sách trung gian (bạn có thể xóa tham gia và biểu thức sẽ trở nên lười biếng, vì mỗi nhóm là một trình vòng lặp)


4

Thay vì sử dụng chức năng mô-đun re.split, bạn có thể đạt được kết quả tương tự bằng cách sử dụng phương pháp loạt.str.split của gấu trúc.

Đầu tiên, tạo một chuỗi với chuỗi trên và sau đó áp dụng phương thức cho chuỗi.

thestring = pd.Series("Hey, you - what are you doing here!?") thestring.str.split(pat = ',|-')

tham số pat lấy các dấu phân cách và trả về chuỗi phân tách dưới dạng một mảng. Ở đây hai dấu phân cách được truyền bằng cách sử dụng một | (hoặc nhà điều hành). Đầu ra như sau:

[Hey, you , what are you doing here!?]


1
Đây không phải là vấn đề dài dòng, mà thực tế là nhập toàn bộ thư viện (mà tôi yêu thích, BTW) để thực hiện một nhiệm vụ đơn giản sau khi chuyển đổi chuỗi thành chuỗi gấu trúc. Không thân thiện & quot; Occam thân thiện & quot;.
zar3bski

3

Tôi đang làm quen lại với Python và cần điều tương tự. Giải pháp findall có thể tốt hơn, nhưng tôi đã nghĩ ra điều này:

tokens = [x.strip() for x in data.split(',')]

Thông minh, nên làm việc trên tất cả các cấu trúc ngữ pháp tiếng Anh mà tôi có thể nghĩ đến ngoại trừ một em-dash không có khoảng trắng. (Giải pháp thay thế.)
ninjagecko

3

sử dụng maketrans và dịch bạn có thể làm điều đó một cách dễ dàng và gọn gàng

import string
specials = ',.!?:;"()<>[]#$=-/'
trans = string.maketrans(specials, ' '*len(specials))
body = body.translate(trans)
words = body.strip().split()

Câu trả lời tuyệt vời như đối với Python> = 3.6
revliscano

3

Trong Python 3, bạn có thể sử dụng phương thức từ PY4E - Python cho mọi người .

Chúng tôi có thể giải quyết tất cả những vấn đề này bằng cách sử dụng các phương pháp chuỗi lower, punctuationtranslate. Đây translatelà tinh tế nhất của các phương pháp. Đây là tài liệu cho translate:

your_string.translate(your_string.maketrans(fromstr, tostr, deletestr))

Thay thế các ký tự fromstrbằng ký tự ở cùng vị trí tostrvà xóa tất cả các ký tự trong đó deletestr. Các fromstrtostrcó thể là các chuỗi rỗng và deletestrtham số có thể được bỏ qua.

Bạn có thể thấy "dấu câu":

In [10]: import string

In [11]: string.punctuation
Out[11]: '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'  

Ví dụ của bạn:

In [12]: your_str = "Hey, you - what are you doing here!?"

In [13]: line = your_str.translate(your_str.maketrans('', '', string.punctuation))

In [14]: line = line.lower()

In [15]: words = line.split()

In [16]: print(words)
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Để biết thêm thông tin, bạn có thể tham khảo:


2
Các phương thức dịch của chuỗi () và maketrans () rất thú vị, nhưng phương thức này không "phân tách tại các dấu phân cách" (hoặc khoảng trắng): ví dụ: "Có một hang động lớn" sẽ tạo ra từ "cavein" không chính xác của "hang" và "trong" dự kiến ​​như vậy, điều này không làm được những gì câu hỏi yêu cầu.
Eric O Lebigot

Đúng như những gì @EricLebigot bình luận. Phương pháp trên không làm được những gì câu hỏi yêu cầu rất tốt.
Jeremy Anifacc

2

Một cách khác để đạt được điều này là sử dụng Bộ công cụ ngôn ngữ tự nhiên ( nltk ).

import nltk
data= "Hey, you - what are you doing here!?"
word_tokens = nltk.tokenize.regexp_tokenize(data, r'\w+')
print word_tokens

Bản in này: ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Hạn chế lớn nhất của phương pháp này là bạn cần cài đặt gói nltk .

Lợi ích là bạn có thể làm rất nhiều thứ thú vị với phần còn lại của gói nltk sau khi bạn nhận được mã thông báo.


1

Trước hết, tôi không nghĩ rằng ý định của bạn là thực sự sử dụng dấu chấm câu làm dấu phân cách trong các hàm phân tách. Mô tả của bạn cho thấy rằng bạn chỉ muốn loại bỏ dấu câu khỏi chuỗi kết quả.

Tôi bắt gặp điều này khá thường xuyên và giải pháp thông thường của tôi không yêu cầu lại.

Chức năng lambda một lớp lót w / danh sách hiểu:

(yêu cầu import string):

split_without_punc = lambda text : [word.strip(string.punctuation) for word in 
    text.split() if word.strip(string.punctuation) != '']

# Call function
split_without_punc("Hey, you -- what are you doing?!")
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']


Chức năng (truyền thống)

Là một chức năng truyền thống, đây vẫn chỉ là hai dòng với sự hiểu biết danh sách (ngoài import string):

def split_without_punctuation2(text):

    # Split by whitespace
    words = text.split()

    # Strip punctuation from each word
    return [word.strip(ignore) for word in words if word.strip(ignore) != '']

split_without_punctuation2("Hey, you -- what are you doing?!")
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']

Nó cũng sẽ tự nhiên để lại các cơn co thắt và các từ gạch nối còn nguyên vẹn. Bạn luôn có thể sử dụng text.replace("-", " ")để biến dấu gạch nối thành khoảng trắng trước khi phân tách.

Chức năng chung w / o Lambda hoặc Danh sách hiểu

Đối với một giải pháp tổng quát hơn (nơi bạn có thể chỉ định các ký tự cần loại bỏ) và không có sự hiểu biết danh sách, bạn nhận được:

def split_without(text: str, ignore: str) -> list:

    # Split by whitespace
    split_string = text.split()

    # Strip any characters in the ignore string, and ignore empty strings
    words = []
    for word in split_string:
        word = word.strip(ignore)
        if word != '':
            words.append(word)

    return words

# Situation-specific call to general function
import string
final_text = split_without("Hey, you - what are you doing?!", string.punctuation)
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']

Tất nhiên, bạn luôn có thể khái quát hàm lambda cho bất kỳ chuỗi ký tự được chỉ định nào.


1

Trước hết, luôn luôn sử dụng re.compile () trước khi thực hiện bất kỳ thao tác RegEx nào trong một vòng lặp vì nó hoạt động nhanh hơn hoạt động bình thường.

Vì vậy, đối với vấn đề của bạn trước tiên hãy biên dịch mẫu và sau đó thực hiện hành động trên nó.

import re
DATA = "Hey, you - what are you doing here!?"
reg_tok = re.compile("[\w']+")
print reg_tok.findall(DATA)

1

Dưới đây là câu trả lời với một số lời giải thích.

st = "Hey, you - what are you doing here!?"

# replace all the non alpha-numeric with space and then join.
new_string = ''.join([x.replace(x, ' ') if not x.isalnum() else x for x in st])
# output of new_string
'Hey  you  what are you doing here  '

# str.split() will remove all the empty string if separator is not provided
new_list = new_string.split()

# output of new_list
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

# we can join it to get a complete string without any non alpha-numeric character
' '.join(new_list)
# output
'Hey you what are you doing'

hoặc trong một dòng, chúng ta có thể làm như thế này:

(''.join([x.replace(x, ' ') if not x.isalnum() else x for x in st])).split()

# output
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

cập nhật câu trả lời


1

Tạo một hàm lấy hai chuỗi đầu vào (chuỗi nguồn được phân tách và chuỗi phân tách danh sách phân cách) và đưa ra danh sách các từ được phân tách:

def split_string(source, splitlist):
    output = []  # output list of cleaned words
    atsplit = True
    for char in source:
        if char in splitlist:
            atsplit = True
        else:
            if atsplit:
                output.append(char)  # append new word after split
                atsplit = False
            else: 
                output[-1] = output[-1] + char  # continue copying characters until next split
    return output

1

Tôi thích giải pháp của pprzemek vì nó không cho rằng các dấu phân cách là các ký tự đơn và nó không cố gắng tận dụng một biểu thức chính quy (sẽ không hoạt động tốt nếu số lượng dấu phân cách bị điên dài).

Đây là một phiên bản dễ đọc hơn của giải pháp trên cho rõ ràng:

def split_string_on_multiple_separators(input_string, separators):
    buffer = [input_string]
    for sep in separators:
        strings = buffer
        buffer = []  # reset the buffer
        for s in strings:
            buffer = buffer + s.split(sep)

    return buffer

0

gặp vấn đề tương tự như @ooboo và tìm thấy chủ đề này @ ghostdog74 đã truyền cảm hứng cho tôi, có lẽ ai đó tìm thấy giải pháp của tôi hữu ích

str1='adj:sg:nom:m1.m2.m3:pos'
splitat=':.'
''.join([ s if s not in splitat else ' ' for s in str1]).split()

nhập một cái gì đó vào vị trí không gian và phân tách bằng cùng một ký tự nếu bạn không muốn phân tách tại các khoảng trắng.


Nếu tôi phải phân chia bằng từ thì sao?
Harsha Biyani

0

Đây là phần của tôi với sự phân chia với nhiều dấu phân cách:

def msplit( str, delims ):
  w = ''
  for z in str:
    if z not in delims:
        w += z
    else:
        if len(w) > 0 :
            yield w
        w = ''
  if len(w) > 0 :
    yield w

0

Tôi nghĩ rằng sau đây là câu trả lời tốt nhất để đáp ứng nhu cầu của bạn:

\W+ có thể phù hợp với trường hợp này, nhưng có thể không phù hợp với các trường hợp khác.

filter(None, re.compile('[ |,|\-|!|?]').split( "Hey, you - what are you doing here!?")

Tôi đồng ý, \w\Wcác giải pháp không phải là một câu trả lời cho (tiêu đề) của câu hỏi. Lưu ý rằng trong câu trả lời của bạn, |nên được loại bỏ (bạn đang nghĩ đến expr0|expr1thay vì [char0 char1…]). Hơn nữa, không cần compile()biểu thức chính quy.
Eric O Lebigot

0

Đây là của tôi về nó ....

def split_string(source,splitlist):
    splits = frozenset(splitlist)
    l = []
    s1 = ""
    for c in source:
        if c in splits:
            if s1:
                l.append(s1)
                s1 = ""
        else:
            print s1
            s1 = s1 + c
    if s1:
        l.append(s1)
    return l

>>>out = split_string("First Name,Last Name,Street Address,City,State,Zip Code",",")
>>>print out
>>>['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']

0

Tôi thích replace()cách tốt nhất. Quy trình sau đây thay đổi tất cả các dấu phân cách được xác định trong một chuỗi splitlistthành dấu phân cách đầu tiên splitlistvà sau đó phân tách văn bản trên một dấu phân cách đó. Nó cũng chiếm nếu splitlistxảy ra là một chuỗi rỗng. Nó trả về một danh sách các từ, không có chuỗi trống trong đó.

def split_string(text, splitlist):
    for sep in splitlist:
        text = text.replace(sep, splitlist[0])
    return filter(None, text.split(splitlist[0])) if splitlist else [text]

0
def get_words(s):
    l = []
    w = ''
    for c in s.lower():
        if c in '-!?,. ':
            if w != '': 
                l.append(w)
            w = ''
        else:
            w = w + c
    if w != '': 
        l.append(w)
    return l

Đây là cách sử dụng:

>>> s = "Hey, you - what are you doing here!?"
>>> print get_words(s)
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

0

Nếu bạn muốn một hoạt động đảo ngược (bảo toàn các dấu phân cách), bạn có thể sử dụng chức năng này:

def tokenizeSentence_Reversible(sentence):
    setOfDelimiters = ['.', ' ', ',', '*', ';', '!']
    listOfTokens = [sentence]

    for delimiter in setOfDelimiters:
        newListOfTokens = []
        for ind, token in enumerate(listOfTokens):
            ll = [([delimiter, w] if ind > 0 else [w]) for ind, w in enumerate(token.split(delimiter))]
            listOfTokens = [item for sublist in ll for item in sublist] # flattens.
            listOfTokens = filter(None, listOfTokens) # Removes empty tokens: ''
            newListOfTokens.extend(listOfTokens)

        listOfTokens = newListOfTokens

    return listOfTokens

0

Gần đây tôi cần phải làm điều này nhưng muốn một hàm phù hợp với str.splitchức năng thư viện chuẩn , hàm này hoạt động giống như thư viện chuẩn khi được gọi với 0 hoặc 1 đối số.

def split_many(string, *separators):
    if len(separators) == 0:
        return string.split()
    if len(separators) > 1:
        table = {
            ord(separator): ord(separator[0])
            for separator in separators
        }
        string = string.translate(table)
    return string.split(separators[0])

LƯU Ý : Chức năng này chỉ hữu ích khi các dấu phân cách của bạn bao gồm một ký tự đơn (như usecase của tôi).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.