Pisahkan String menjadi kata-kata dengan beberapa batas pembatas kata

671

Saya pikir apa yang ingin saya lakukan adalah tugas yang cukup umum tetapi saya tidak menemukan referensi di web. Saya memiliki teks dengan tanda baca, dan saya ingin daftar kata-kata.

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

seharusnya

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

Tapi Python str.split()hanya bekerja dengan satu argumen, jadi saya memiliki semua kata dengan tanda baca setelah saya berpisah dengan spasi putih. Ada ide?

ooboo
sumber
6
python str.split()juga bekerja tanpa argumen sama sekali
Ivan Vinogradov

Jawaban:

468

Kasus di mana ekspresi reguler dibenarkan:

import re
DATA = "Hey, you - what are you doing here!?"
print re.findall(r"[\w']+", DATA)
# Prints ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']
RichieHindle
sumber
2
Terima kasih. Namun masih tertarik - bagaimana saya bisa mengimplementasikan algoritma yang digunakan dalam modul ini? Dan mengapa itu tidak muncul dalam modul string?
ooboo
29
Ekspresi reguler bisa menakutkan pada awalnya, tetapi sangat kuat. Ekspresi reguler '\ w +' berarti "karakter kata (az dll) diulang satu atau lebih kali". Ada HOWTO pada ekspresi reguler Python di sini: amk.ca/python/howto/regex
RichieHindle
324
Ini bukan jawaban untuk pertanyaan itu. Ini adalah jawaban untuk pertanyaan yang berbeda, yang kebetulan bekerja untuk situasi khusus ini. Seolah-olah seseorang bertanya "bagaimana saya belok kiri" dan jawaban terpilih adalah "ambil tiga belokan kanan berikutnya." Ini berfungsi untuk persimpangan tertentu, tetapi tidak memberikan jawaban yang dibutuhkan. Ironisnya, jawabannya adalah di re, hanya saja tidak findall. Jawaban di bawah memberi re.split()lebih unggul.
Jesse Dhillon
4
@JesseDhillon "mengambil semua substring yang terdiri dari urutan karakter kata" dan "split pada semua substring yang terdiri dari urutan karakter non-kata" secara harfiah hanya cara berbeda untuk mengekspresikan operasi yang sama; Saya tidak yakin mengapa Anda akan memanggil salah satu dari keduanya sebagai jawaban superior.
Mark Amery
4
@TMWP: Apostophe berarti bahwa kata seperti don'tdiperlakukan sebagai satu kata, daripada dipecah menjadi dondan t.
RichieHindle
574

re.split ()

re.split (pola, string [, maxsplit = 0])

Pisahkan string dengan kemunculan pola. Jika menangkap tanda kurung digunakan dalam pola, maka teks semua grup dalam pola juga dikembalikan sebagai bagian dari daftar yang dihasilkan. Jika maxsplit bukan nol, paling banyak terjadi perpecahan maxsplit, dan sisa string dikembalikan sebagai elemen terakhir dari daftar. (Catatan Ketidakcocokan: dalam rilis Python 1.5 asli, maxsplit diabaikan. Ini telah diperbaiki pada rilis selanjutnya.)

>>> 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.']
Gimel
sumber
13
Solusi ini memiliki keuntungan dengan mudah diadaptasi untuk dipecah pada garis bawah juga, sesuatu yang tidak ditemukan oleh solusi all: print re.split ("\ W + | _", "Pengujian this_thing") 'hasil: [' Pengujian ',' ini ' , 'thing']
Emil Stenström
63
Sekarang jika saja aku bisa mengingat perbedaan antara \w, \W, \s, dan \S. Siapa pun yang berpikir bahwa kapitalisasi bendera harus membalik maknanya harus ditembakkan melalui kepala.
ArtOfWarfare
1
Kasus pemisahan string yang umum digunakan adalah menghapus entri string kosong dari hasil akhir. Apakah mungkin melakukannya dengan metode ini? re.split ('\ W +', 'abc') menghasilkan ['', 'a', 'b', 'c', '']
Scott Morken
3
@ ArtOfWarfare Adalah umum untuk menggunakan shiftkunci untuk melakukan kebalikan dari sesuatu. ctrl+zbatalkan vs ctrl+shift+zuntuk redo. Jadi shift w, atau W, akan menjadi kebalikan dari w.
Frank Vel
1
Jawaban ini harus di atas - itu adalah satu-satunya yang tepat menjawab judul pertanyaan.
Kranach
381

Cara cepat lain untuk melakukan ini tanpa regexp adalah mengganti karakter terlebih dahulu, seperti di bawah ini:

>>> 'a;bcd,ef g'.replace(';',' ').replace(',',' ').split()
['a', 'bcd', 'ef', 'g']
Louis LC
sumber
71
Cepat dan kotor tetapi cocok untuk kasus saya (alat pemisah saya adalah perangkat kecil yang dikenal)
Andy Baker
7
Sempurna untuk kasus di mana Anda tidak memiliki akses ke perpustakaan RE, seperti mikrokontroler kecil tertentu. :-)
tu-Reinstate Monica-dor duh
11
Saya pikir ini lebih eksplisit daripada RE juga, jadi ini semacam noob friendly. Terkadang tidak membutuhkan solusi umum untuk semuanya
Adam Hughes
Luar biasa. Saya memiliki .split () dalam situasi input ganda, dan perlu menangkap ketika pengguna, saya, memisahkan input dengan spasi dan bukan koma. Saya hampir menyerah dan menyusun kembali dengan re, tetapi solusi .replace (Anda) mengenai kuku di kepala. Terima kasih.
JayJay123
itu akan membuat Anda salah menjawab ketika Anda tidak ingin membagi pada spasi dan Anda ingin membagi pada karakter lain.
Ahmed Amr
307

Begitu banyak jawaban, namun saya tidak dapat menemukan solusi yang efisien apa yang diminta oleh judul pertanyaan (memisahkan pada beberapa pemisah yang mungkin — sebagai gantinya, banyak jawaban terpecah pada apa pun yang bukan kata, yang berbeda). Jadi di sini adalah jawaban untuk pertanyaan dalam judul, yang bergantung pada remodul standar dan efisien 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']

dimana:

  • yang […]pertandingan salah satu pemisah yang terdaftar di dalam,
  • yang \-dalam ekspresi reguler di sini untuk mencegah interpretasi khusus -sebagai indikator berbagai karakter (seperti dalam A-Z),
  • yang +melompat satu atau lebih pembatas (itu bisa dihilangkan berkat filter(), tapi ini tidak perlu akan menghasilkan string kosong antara pemisah cocok), dan
  • filter(None, …) menghapus string kosong yang mungkin dibuat oleh pemisah leading dan trailing (karena string kosong memiliki nilai boolean palsu).

Ini re.split()tepatnya "terbagi dengan beberapa pemisah", seperti yang diminta dalam judul pertanyaan.

Solusi ini juga kebal terhadap masalah dengan karakter non-ASCII dalam kata-kata yang ditemukan dalam beberapa solusi lain (lihat komentar pertama untuk jawaban ghostdog74 ).

The reModul jauh lebih efisien (dalam kecepatan dan amputasi) daripada melakukan Python loop dan tes "dengan tangan"!

Eric O Lebigot
sumber
3
"Saya tidak dapat menemukan solusi yang efisien apa yang ditanyakan oleh judul pertanyaan itu" - jawaban kedua melakukannya, diposting 5 tahun lalu: stackoverflow.com/a/1059601/2642204 .
BartoszKP
17
Jawaban ini tidak terpecah pada pembatas (dari satu set pembatas ganda): ia malah membagi apa pun yang bukan alfanumerik. Yang mengatakan, saya setuju bahwa maksud dari poster asli mungkin hanya menyimpan kata-kata, bukan menghilangkan beberapa tanda baca.
Eric O Lebigot
EOL: Saya pikir jawaban ini terpecah pada seperangkat beberapa delimeter. Jika Anda menambahkan non-alfanumerik ke string yang tidak ditentukan, seperti garis bawah, mereka tidak terpecah, seperti yang diharapkan.
GravityWell
@ GravityWell: Saya tidak yakin saya mengerti: dapatkah Anda memberikan contoh nyata?
Eric O Lebigot
3
@ EOL: Saya baru menyadari bahwa saya bingung dengan komentar Anda "Jawaban ini tidak terpecah ..." Saya pikir "ini" merujuk pada jawaban Anda. Saya pikir jawaban INI (jawaban yang saya komentari) adalah jawaban terbaik :)
GravityWell
56

Cara lain, tanpa 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()
ghostdog74
sumber
8
Solusi ini sebenarnya lebih baik daripada yang diterima. Ini berfungsi tanpa karakter ASCII, cobalah "Hey, you - what are you doing here María!?". Solusi yang diterima tidak akan berfungsi dengan contoh sebelumnya.
Christopher Ramírez
4
Saya pikir ada masalah kecil di sini ... Kode Anda akan menambahkan karakter yang dipisahkan dengan tanda baca dan dengan demikian tidak akan membagi mereka ... Jika saya tidak salah, baris terakhir Anda harus:''.join([o if not o in string.punctuation else ' ' for o in s]).split()
cedbeu
Pustaka ekspresi reguler dapat dibuat untuk menerima konvensi Unicode untuk karakter jika perlu. Selain itu, ini memiliki masalah yang sama dengan solusi yang diterima untuk digunakan: seperti sekarang, ia terbagi pada apostrof. Anda mungkin ingin o for o in s if (o in not string.punctuation or o == "'"), tetapi kemudian menjadi terlalu rumit untuk satu-liner jika kita menambahkan patch cedbeu juga.
Daniel H
Ada masalah lain di sini. Bahkan ketika kita memperhitungkan perubahan @cedbeu, kode ini tidak berfungsi jika stringnya seperti "First Name,Last Name,Street Address,City,State,Zip Code"dan kita ingin membaginya hanya dengan koma ,. Output yang diinginkan adalah: ['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']Apa yang kita dapatkan sebagai gantinya:['First', 'Name', 'Last', 'Name', 'Street', 'Address', 'City', 'State', 'Zip', 'Code']
Stefan van den Akker
4
Solusi ini sangat tidak efisien: pertama daftar didekonstruksi menjadi karakter individu, kemudian seluruh rangkaian karakter tanda baca dilalui untuk setiap karakter tunggal dalam string asli, kemudian karakter tersebut dirakit kembali, dan kemudian dipecah lagi. Semua "gerakan" ini juga sangat rumit, dibandingkan dengan solusi berbasis ekspresi reguler: bahkan jika kecepatan tidak masalah dalam aplikasi yang diberikan, tidak perlu untuk solusi yang rumit. Karena remodul ini standar dan memberikan keterbacaan dan kecepatan, saya tidak mengerti mengapa itu harus dihindari.
Eric O Lebigot
39

Pro-Tip: Gunakan string.translateuntuk operasi string tercepat yang dimiliki Python.

Beberapa bukti ...

Pertama, cara lambat (maaf 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

Selanjutnya, kami menggunakan re.findall()(seperti yang diberikan oleh jawaban yang disarankan). Lebih cepat:

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

Akhirnya, kami menggunakan 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

Penjelasan:

string.translatediimplementasikan dalam C dan tidak seperti banyak fungsi manipulasi string di Python, string.translate tidak menghasilkan string baru. Jadi ini tentang secepat Anda bisa mendapatkan untuk penggantian string.

Ini agak canggung, karena membutuhkan tabel terjemahan untuk melakukan keajaiban ini. Anda dapat membuat tabel terjemahan dengan maketrans()fungsi kenyamanan. Tujuannya di sini adalah untuk menerjemahkan semua karakter yang tidak diinginkan ke spasi. Pengganti satu-satu. Sekali lagi, tidak ada data baru yang dihasilkan. Jadi ini cepat !

Selanjutnya, kami menggunakan tua yang baik split(). split()secara default akan beroperasi pada semua karakter spasi, mengelompokkannya untuk pemisahan. Hasilnya akan menjadi daftar kata yang Anda inginkan. Dan pendekatan ini hampir 4x lebih cepat dari re.findall()!

Dave
sumber
4
Saya membuat tes di sini, dan jika Anda perlu menggunakan unicode, menggunakan patt = re.compile(ur'\w+', re.UNICODE); patt.findall(S)lebih cepat daripada menerjemahkan, karena Anda harus menyandikan string sebelum menerapkan transformasi, dan mendekode setiap item dalam daftar setelah pemisahan untuk kembali ke unicode.
Rafael S. Calsaverini
Anda dapat melakukan one-liner implementasi terjemahan dan memastikan bahwa S tidak termasuk di antara splitter dengan:s.translate(''.join([(chr(i) if chr(i) not in seps else seps[0]) for i in range(256)])).split(seps[0])
hobs
Tidak ada yang diambil. Anda membandingkan apel dan jeruk. ;) solusi saya di python 3 masih berfungsi; P dan memiliki dukungan untuk pemisah multi-char. :) coba lakukan dengan cara sederhana tanpa mengalokasikan string baru. :) tapi benar, punyaku terbatas pada penguraian baris perintah dan bukan buku misalnya.
pprzemek
Anda mengatakan "tidak menghasilkan string baru", yang berarti itu bekerja di tempat pada string yang diberikan? Saya mengujinya sekarang dengan python 2.7 dan tidak mengubah string oroginal dan mengembalikan yang baru.
Prokop Hapala
26

Saya memiliki dilema yang sama dan tidak ingin menggunakan modul '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']
pprzemek
sumber
1
Saya suka ini. Hanya sebuah catatan, urutan pemisah penting. Maaf kalau itu sudah jelas.
crizCraig
2
Mengapa tidak menggunakan remodul, yang lebih cepat dan lebih jelas (bukan berarti ekspresi reguler sangat jelas, tetapi karena cara ini lebih pendek dan langsung)?
Eric O Lebigot
13

Pertama, saya ingin setuju dengan orang lain bahwa regex atau str.translate(...)solusi berbasis paling performan. Untuk kasus penggunaan saya, kinerja fungsi ini tidak signifikan, jadi saya ingin menambahkan ide yang saya pertimbangkan dengan kriteria itu.

Tujuan utama saya adalah untuk menggeneralisasi ide dari beberapa jawaban lain menjadi satu solusi yang dapat bekerja untuk string yang berisi lebih dari sekedar kata regex (yaitu, daftar hitam subset eksplisit karakter tanda baca vs karakter kata daftar putih).

Perhatikan bahwa, dalam pendekatan apa pun, orang mungkin juga mempertimbangkan untuk menggunakan string.punctuationmenggantikan daftar yang ditentukan secara manual.

Opsi 1 - re.sub

Saya terkejut melihat tidak ada jawaban sejauh ini menggunakan re.sub (...) . Saya menemukan ini pendekatan yang sederhana dan alami untuk masalah ini.

import re

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

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

Dalam solusi ini, saya menyarangkan panggilan ke re.sub(...)dalam re.split(...)- tetapi jika kinerja sangat penting, mengkompilasi regex di luar bisa bermanfaat - untuk kasus penggunaan saya, perbedaannya tidak signifikan, jadi saya lebih suka kesederhanaan dan keterbacaan.

Opsi 2 - str.replace

Ini adalah beberapa baris lagi, tetapi memiliki keuntungan diperluas tanpa harus memeriksa apakah Anda perlu melarikan diri dari karakter tertentu di regex.

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

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

words = my_str.split()

Akan lebih baik untuk dapat memetakan str.replace ke string sebagai gantinya, tapi saya tidak berpikir itu bisa dilakukan dengan string yang tidak dapat diubah, dan sementara memetakan terhadap daftar karakter akan bekerja, menjalankan setiap penggantian terhadap setiap karakter terdengar berlebihan. (Edit: Lihat opsi selanjutnya untuk contoh fungsional.)

Opsi 3 - functools.reduce

(Dalam Python 2, reducetersedia di namespace global tanpa mengimpornya dari functools.)

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()
Taylor Edmiston
sumber
Hm, satu metode lain adalah menggunakan str.translate- itu bukan unicode-mampu tetapi kemungkinan besar lebih cepat daripada metode lain dan karena itu mungkin baik dalam beberapa kasus: replacements=',-!?'; import string; my_str = my_str.translate(string.maketrans(replacements, ' ' * len(replacements)))Juga di sini adalah wajib untuk memiliki penggantian sebagai rangkaian karakter, bukan tuple atau daftar.
MarSoft
@ MarsSoft, Terima kasih! Saya menyebutkan itu di bagian atas jawaban tetapi memutuskan untuk tidak menambahkannya karena jawaban yang ada sudah membahasnya dengan baik.
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]

Maka ini menjadi tiga baris:

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

Penjelasan

Inilah yang di Haskell dikenal sebagai List monad. Gagasan di balik monad adalah bahwa sekali "di monad" Anda "tetap di monad" sampai sesuatu membawa Anda keluar. Misalnya di Haskell, katakan Anda memetakan range(n) -> [1,2,...,n]fungsi python di atas Daftar. Jika hasilnya Daftar, itu akan ditambahkan ke Daftar di tempat, sehingga Anda akan mendapatkan sesuatu seperti map(range, [3,4,1]) -> [0,1,2,0,1,2,3,0]. Ini dikenal sebagai peta-append (atau mappend, atau mungkin sesuatu seperti itu). Idenya di sini adalah bahwa Anda memiliki operasi ini yang Anda terapkan (membagi pada token), dan setiap kali Anda melakukannya, Anda bergabung hasilnya ke dalam daftar.

Anda dapat mengabstraksi ini menjadi fungsi dan tokens=string.punctuationsecara default.

Keuntungan dari pendekatan ini:

  • Pendekatan ini (tidak seperti pendekatan berbasis regex naif) dapat bekerja dengan token sewenang-wenang (yang regex juga dapat lakukan dengan sintaksis yang lebih maju).
  • Anda tidak terbatas pada token belaka; Anda dapat memiliki logika arbitrer sebagai ganti masing-masing token, misalnya salah satu dari "token" bisa berupa fungsi yang terbagi sesuai dengan bagaimana tanda kurung bersarang.
ninjagecko
sumber
Solusi Haskell yang rapi, tetapi IMO ini dapat ditulis lebih jelas tanpa mappend dengan Python.
Vlad the Impala
@ Goose: intinya adalah fungsi 2-line map_then_appenddapat digunakan untuk membuat masalah menjadi 2-liner, serta banyak masalah lain yang lebih mudah ditulis. Sebagian besar solusi lain menggunakan remodul ekspresi reguler , yang bukan python. Tapi saya tidak senang dengan bagaimana saya membuat jawaban saya tampak tidak menarik dan menggembung ketika itu benar-benar singkat ... Saya akan mengeditnya ...
ninjagecko
apakah ini seharusnya bekerja dengan Python seperti yang tertulis? fragmentshasil saya hanyalah daftar karakter dalam string (termasuk token).
Rick mendukung Monica
@ RickTeachey: ini bekerja untuk saya di kedua python2 dan python3.
ninjagecko
hmmmm Mungkin contohnya agak ambigu. Saya telah mencoba kode dalam jawaban segala macam cara-yang berbeda termasuk memiliki fragments = ['the,string'], fragments = 'the,string'atau fragments = list('the,string')dan tidak satupun dari mereka yang menghasilkan output yang benar.
Rick mendukung Monica
5

coba ini:

import re

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

ini akan dicetak ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Corey Goldberg
sumber
4

Gunakan ganti dua kali:

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

menghasilkan:

['11223', '33344', '33222', '3344']
jeroen
sumber
4

Saya suka re , tapi ini solusi saya tanpanya:

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]

September .__ berisi__ adalah metode yang digunakan oleh operator 'in'. Pada dasarnya sama dengan

lambda ch: ch in sep

tetapi lebih nyaman di sini.

groupby mendapatkan string dan fungsi kami. Ini membagi string dalam kelompok menggunakan fungsi itu: setiap kali nilai fungsi berubah - grup baru dihasilkan. Jadi, September .__ berisi__ persis apa yang kita butuhkan.

groupby mengembalikan urutan pasangan, di mana pasangan [0] adalah hasil dari fungsi kami dan pasangan [1] adalah grup. Menggunakan 'jika bukan k' kami memfilter grup dengan pemisah (karena hasil September .__ berisi__ adalah True on separator). Yah, itu saja - sekarang kita memiliki urutan grup di mana masing-masing adalah kata (grup sebenarnya adalah iterable jadi kami menggunakan gabungan untuk mengubahnya menjadi string).

Solusi ini cukup umum, karena menggunakan fungsi untuk memisahkan string (Anda dapat membaginya dengan kondisi apa pun yang Anda butuhkan). Juga, itu tidak membuat string / daftar menengah (Anda dapat menghapus bergabung dan ekspresi akan menjadi malas, karena setiap grup adalah iterator)

monitorius
sumber
4

Alih-alih menggunakan fungsi modul re.split Anda dapat mencapai hasil yang sama dengan menggunakan metode panda series.str.split.

Pertama, buat seri dengan string di atas dan kemudian terapkan metode ke seri.

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

pat parameter mengambil pembatas dan mengembalikan string split sebagai array. Di sini dua pembatas dilewatkan menggunakan | (atau operator). Outputnya adalah sebagai berikut:

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

Tarun Kumar Yellapu
sumber
1
Ini bukan soal verbose melainkan fakta mengimpor seluruh perpustakaan (yang saya sukai, BTW) untuk melakukan tugas sederhana setelah mengonversi string ke seri panda. Tidak terlalu & quot; Ramah di tempat & quot ;.
zar3bski
3

Saya kembali berkenalan dengan Python dan membutuhkan hal yang sama. Solusi findall mungkin lebih baik, tetapi saya datang dengan ini:

tokens = [x.strip() for x in data.split(',')]
Leon Starr
sumber
Pintar, harus bekerja pada semua konstruksi tata bahasa Inggris yang bisa saya pikirkan kecuali em-dash tanpa spasi — ini, misalnya. (Dapat
diperbaiki
3

menggunakan maketrans dan terjemahkan Anda dapat melakukannya dengan mudah dan rapi

import string
specials = ',.!?:;"()<>[]#$=-/'
trans = string.maketrans(specials, ' '*len(specials))
body = body.translate(trans)
words = body.strip().split()
Ritesh Sinha
sumber
Jawaban yang bagus untuk Python> = 3.6
revliscano
3

Di Python 3, Anda bisa menggunakan metode dari PY4E - Python untuk Semua Orang .

Kita dapat memecahkan kedua masalah ini dengan menggunakan metode tali lower, punctuationdan translate. Ini translateadalah metode yang paling halus. Ini dokumentasi untuk translate:

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

Ganti karakter fromstrdengan karakter di posisi yang sama tostrdan hapus semua karakter yang ada di deletestr. The fromstrdan tostrdapat string kosong dan deletestrparameter dapat dihilangkan.

Anda dapat melihat "tanda baca":

In [10]: import string

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

Sebagai contoh Anda:

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

Untuk informasi lebih lanjut, Anda dapat merujuk:

Jeremy Anifacc
sumber
2
Metode string translate () dan maketrans () menarik, tetapi metode ini gagal untuk "membagi pada pembatas" (atau spasi putih): misalnya, "Ada gua-in besar" sebagai gantinya akan menghasilkan kata "cavein" yang salah dari "gua" dan "dalam" yang diharapkan ... Dengan demikian, ini tidak melakukan apa yang diminta pertanyaan.
Eric O Lebigot
Sama seperti yang dikomentari @EricLebigot. Metode di atas tidak melakukan apa yang diminta pertanyaan dengan sangat baik.
Jeremy Anifacc
2

Cara lain untuk mencapai ini adalah dengan menggunakan Natural Language Tool Kit ( nltk ).

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

Ini mencetak: ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Kelemahan terbesar dari metode ini adalah Anda harus menginstal paket nltk .

Keuntungannya adalah Anda dapat melakukan banyak hal menyenangkan dengan sisa paket nltk begitu Anda mendapatkan token Anda.

tgray
sumber
1

Pertama-tama, saya tidak berpikir bahwa maksud Anda adalah untuk benar-benar menggunakan tanda baca sebagai pembatas dalam fungsi split. Deskripsi Anda menunjukkan bahwa Anda hanya ingin menghilangkan tanda baca dari string yang dihasilkan.

Saya menemukan ini cukup sering, dan solusi yang biasa saya tidak perlu kembali.

Fungsi lambda satu-lapis dengan pemahaman daftar:

(membutuhkan 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']


Fungsi (tradisional)

Sebagai fungsi tradisional, ini masih hanya dua baris dengan pemahaman daftar (selain 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']

Ini juga akan secara alami membuat kontraksi dan kata-kata yang ditulis dengan tanda penghubung utuh. Anda selalu dapat menggunakan text.replace("-", " ")untuk mengubah tanda hubung menjadi spasi sebelum pemisahan.

Fungsi Umum tanpa Lambda atau Daftar Pemahaman

Untuk solusi yang lebih umum (di mana Anda dapat menentukan karakter untuk dihilangkan), dan tanpa pemahaman daftar, Anda mendapatkan:

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

Tentu saja, Anda selalu dapat menggeneralisasi fungsi lambda ke string karakter tertentu juga.

cosmicFluke
sumber
1

Pertama-tama, selalu gunakan re.compile () sebelum melakukan operasi RegEx dalam satu lingkaran karena ia bekerja lebih cepat daripada operasi normal.

jadi untuk masalah Anda pertama-tama kompilasi pola dan kemudian lakukan tindakan padanya.

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

Inilah jawabannya dengan beberapa penjelasan.

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'

atau dalam satu baris, kita bisa melakukan ini:

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

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

jawaban yang diperbarui

Tasneem Haider
sumber
1

Buat fungsi yang mengambil sebagai input dua string (string sumber untuk dipisah dan string pembatas pemisah) dan mengeluarkan daftar kata-kata split:

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
pengguna852006
sumber
1

Saya suka solusi pprzemek karena tidak berasumsi bahwa pembatas adalah karakter tunggal dan tidak mencoba memanfaatkan regex (yang tidak akan bekerja dengan baik jika jumlah pemisah menjadi gila lama).

Berikut ini versi yang lebih mudah dibaca dari solusi di atas untuk kejelasan:

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
Everett
sumber
0

mendapat masalah yang sama dengan @ooboo dan menemukan topik ini @ ghostdog74 menginspirasi saya, mungkin seseorang menemukan solusi saya berguna

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

masukan sesuatu di tempat spasi dan pisah menggunakan karakter yang sama jika Anda tidak ingin membagi di spasi.

badas
sumber
bagaimana jika saya harus membagi menggunakan kata?
Harsha Biyani
0

Ini adalah perjalanan saya dengan beberapa pembatas:

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
Martlark
sumber
0

Saya pikir berikut ini adalah jawaban terbaik untuk menyesuaikan kebutuhan Anda:

\W+ mungkin cocok untuk kasus ini, tetapi mungkin tidak cocok untuk kasus lain.

filter(None, re.compile('[ |,|\-|!|?]').split( "Hey, you - what are you doing here!?")
nemozhp
sumber
Saya setuju, \wdan \Wsolusi bukanlah jawaban untuk (judul) pertanyaan. Perhatikan bahwa dalam jawaban Anda, |harus dihapus (yang Anda pikirkan expr0|expr1bukan [char0 char1…]). Lebih jauh lagi, tidak perlu compile()ekspresi reguler.
Eric O Lebigot
0

Inilah saya mengambilnya ....

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']
Arindam Roychowdhury
sumber
0

Saya suka replace()cara yang terbaik. Prosedur berikut mengubah semua pemisah yang didefinisikan dalam string splitlistmenjadi pemisah pertama di splitlistkemudian memisahkan teks pada satu pemisah itu. Itu juga menjelaskan jika splitlistkebetulan adalah string kosong. Ini mengembalikan daftar kata-kata, tanpa string kosong di dalamnya.

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]
Stefan van den Akker
sumber
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

Ini adalah penggunaannya:

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

Jika Anda ingin operasi yang dapat dibalik (mempertahankan pembatas), Anda dapat menggunakan fungsi ini:

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
Nadav B
sumber
0

Saya baru-baru ini perlu melakukan ini tetapi menginginkan fungsi yang agak cocok dengan str.splitfungsi perpustakaan standar , fungsi ini berperilaku sama dengan perpustakaan standar ketika dipanggil dengan argumen 0 atau 1.

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

CATATAN : Fungsi ini hanya berguna ketika pemisah Anda terdiri dari satu karakter (seperti yang digunakan pengguna saya).

Justin Fay
sumber