Dizeleri birden çok kelime sınırlayıcı ile kelimelere bölme


671

Bence yapmak istediğim şey oldukça yaygın bir iş ama internette hiç referans bulamadım. Noktalama işaretli bir metin var ve kelimelerin bir listesini istiyorum.

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

olmalı

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

Ancak Python str.split()sadece bir argümanla çalışır, bu yüzden boşlukla ayrıldıktan sonra noktalama işaretleriyle ilgili tüm kelimelerim var. Herhangi bir fikir?



6
python's str.split()da hiç argüman olmadan çalışıyor
Ivan Vinogradov

Yanıtlar:


468

Düzenli ifadelerin gerekçelendirildiği bir durum:

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
Teşekkürler. Yine de ilgileniyorum - bu modülde kullanılan algoritmayı nasıl uygulayabilirim? Ve neden string modülünde görünmüyor?
ooboo

29
Düzenli ifadeler ilk başta göz korkutucu olabilir, ancak çok güçlüdür. '\ W +' normal ifadesi "bir veya daha fazla kez yinelenen bir kelime karakteri (az vb.)" Anlamına gelir. Burada Python düzenli ifadelerinde bir NASIL belgesi vardır
RichieHindle

324
Bu sorunun cevabı değil. Bu, bu özel durum için işe yarayan farklı bir sorunun cevabıdır. Sanki birisi "nasıl sola dönüş yapabilirim" diye sordu ve en çok oy alan cevap "sonraki üç sağa dönüş yap" idi. Belirli kavşaklar için çalışır, ancak gerekli cevabı vermez. İronik olarak, cevap ise de re, adil değil findall. Aşağıdaki cevap re.split()üstündür.
Jesse Dhillon

4
@JesseDhillon "bir dizi kelime karakteri içeren tüm alt dizeleri al" ve "bir dizi kelime olmayan karakterden oluşan tüm alt dizelere bölün" tam olarak aynı işlemi ifade etmenin farklı yollarıdır; Neden her ikisine de cevap üstün diyeceğinizden emin değilim.
Mark Amery

4
@TMWP: apostophe aracının gibi bir kelime don'tyerine yarık olmak yerine, tek bir kelime olarak işleme tabi tutulur donve t.
RichieHindle

574

re.split ()

re.split (desen, dize [, maxsplit = 0])

Dizeyi desen oluşumlarına göre bölün. Desende yakalama parantezleri kullanılıyorsa, desendeki tüm grupların metinleri de sonuç listesinin bir parçası olarak döndürülür. Maxsplit sıfırdan farklıysa, en fazla maxsplit bölünmesi oluşur ve dizenin geri kalanı listenin son öğesi olarak döndürülür. (Uyumsuzluk notu: Orijinal Python 1.5 sürümünde maxsplit yok sayıldı. Bu sonraki sürümlerde düzeltildi.)

>>> 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
Bu çözümün avantajı, altçizgileri ayırmak için kolayca adapte olma avantajına sahiptir, findall çözümünün yapmadığı bir şey: print re.split ("\ W + | _", "this_thing'i test etme") 'şunu sağlar: [' Test ',' this ' , 'şey']
Emil Stenström

63
Şimdi keşke ben arasındaki farkı hatırlıyorum \w, \W, \s, ve \S. Bir bayrağın büyük harf kullanımını anlamını tersine çevirmesi gerektiğini kim düşünürse, kafadan vurulmalıdır.
ArtOfWarfare

1
Yaylı dizgi ayırma işleminin yaygın bir kullanımı, boş dizgi girişlerini nihai sonuçtan kaldırmaktır. Bunu bu yöntemle yapmak mümkün mü? re.split ('\ W +', 'abc'), ['', 'a', 'b', 'c', ''] ile
sonuçlanır

3
@ArtOfWarfare Bir shiftşeyin tersini yapmak için anahtarı kullanmak yaygındır . Yinele için ctrl+zgeri al ctrl+shift+z. Yani shift w, ya Wda tersi olur w.
Frank Vel

1
Bu cevap en üstte olmalıdır - soru başlığını tam olarak cevaplayan tek cevaptır.
Kranach

381

Normal ifade olmadan bunu yapmanın bir diğer hızlı yolu, önce aşağıdaki gibi karakterleri değiştirmektir:

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

71
Hızlı ve kirli ama benim durumum için mükemmel (ayırıcılar küçük, bilinen bir setti)
Andy Baker

7
Bazı küçük mikrodenetleyiciler gibi RE kitaplığına erişemediğiniz durumlar için mükemmeldir. :-)
tu-Reinstate Monica-dor duh

11
Bu da RE daha açık olduğunu düşünüyorum, bu yüzden tür çaylak dostu. Bazen her şeye genel bir çözüm bulmak gerekmez
Adam Hughes

Muhteşem. Birden çok giriş durumunda bir .split () vardı ve kullanıcı, ben, girişleri virgülle değil bir boşlukla ayırdığında yakalamak gerekiyordu. Ben vazgeçmek ve re ile yeniden yapmak üzereydim, ama .replace () çözümün kafasına çivi vurdu. Teşekkürler.
JayJay123

boşluklara bölmek istemediğinizde ve diğer karakterlere bölmek istediğinizde size yanlış cevap verecektir.
Ahmed Amr

307

Pek çok cevap, ancak soruların başlığının tam anlamıyla ne istediğini etkili bir şekilde yapan herhangi bir çözüm bulamıyorum (birden fazla olası ayırıcıya bölme - bunun yerine, birçok cevap sözcük olmayan, farklı olan herhangi bir şeye bölündü). İşte Python'un standart ve verimli remodülüne dayanan başlıktaki soruya bir cevap :

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

nerede:

  • […]maçlar biri ayırıcılar içinde listelenen,
  • \-normal ifadede özel bir yorumunu önlemek için burada -(olduğu kadar bir karakter aralığı göstergesi A-Z),
  • +bir atlar ya da daha fazla (o sayesinde atlanmış olabilir ayraçları filter(), ancak bu gereksiz yere eşleşti ayırıcılar arasındaki boş dizeleri üretecektir) ve
  • filter(None, …) önde gelen ve arkadaki ayırıcılar tarafından oluşturulan boş dizeleri kaldırır (boş dizeler yanlış bir boole değerine sahip olduğundan).

Bu re.split(), soru başlığında istendiği gibi, "birden çok ayırıcıyla ayrılır".

Bu çözüm ayrıca diğer bazı çözümlerde bulunan ASCII olmayan karakterlerle ilgili sorunlara karşı bağışıktır ( ghostdog74'ün cevabının ilk yorumuna bakın ).

reModül "elle" Python döngüler ve testler yapmak yerine (hız ve concision olarak) çok daha verimli olduğunu!


3
"Soruların başlığının tam olarak ne istediğini etkili bir şekilde yapan herhangi bir çözüm bulamıyorum" - 5 yıl önce gönderilen ikinci cevap bunu yapıyor: stackoverflow.com/a/1059601/2642204 .
BartoszKP

17
Bu yanıt sınırlayıcılara bölünmez (birden çok sınırlayıcı kümesinden): bunun yerine alfasayısal olmayan herhangi bir şeye bölünür. Bununla birlikte, orijinal posterin amacının bazı noktalama işaretlerini kaldırmak yerine muhtemelen sadece kelimeleri tutmak olduğunu kabul ediyorum.
Eric O Lebigot

EOL: Bence bu cevap bir dizi çoklu sınırlayıcıya bölündü. Alt çizgi gibi belirtilmeyen dizeye alfasayısal olmayanlar eklerseniz, beklendiği gibi bölünmezler.
GravityWell

@GravityWell: Anladığımdan emin değilim: somut bir örnek verebilir misiniz?
Eric O Lebigot

3
@EOL: "Bu cevap bölünmez ..." yorumunuzla kafam karıştığımı fark ettim. Bence bu cevap (yorum yaptığım cevap) en iyi cevap :)
GravityWell

56

Normal ifade olmadan başka bir yol

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
Bu çözüm aslında kabul edilenden daha iyidir. ASCII karakterleri olmadan çalışır, deneyin "Hey, you - what are you doing here María!?". Kabul edilen çözüm önceki örnekle çalışmaz.
Christopher Ramírez

4
Sanırım burada küçük bir sorun var ... Kodunuz noktalama işaretleriyle ayrılmış karakterler ekleyecek ve böylece onları ''.join([o if not o in string.punctuation else ' ' for o in s]).split()
bölmeyecek

Gerekirse karakterler için Unicode kurallarını kabul etmek üzere normal ifade kütüphanesi yapılabilir. Buna ek olarak, bu kabul edilen çözümün sahip olduğu problemle aynıdır: şimdi olduğu gibi kesme işaretlerine ayrılır. Sen isteyebilirsiniz o for o in s if (o in not string.punctuation or o == "'"), ama biz de cedbeu yamayla içinde eklerseniz o zaman çok tek astar için karmaşık vurmayacak.
Daniel H

Burada başka bir sorun daha var. @Cedbeu'daki değişiklikleri hesaba kattığımızda bile, dize benzer bir şeyse "First Name,Last Name,Street Address,City,State,Zip Code"ve yalnızca virgül üzerinde bölmek istiyorsak bu kod çalışmaz ,. İstenilen çıktı şöyle olur: ['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']Bunun yerine alacağımız şey:['First', 'Name', 'Last', 'Name', 'Street', 'Address', 'City', 'State', 'Zip', 'Code']
Stefan van den Akker

4
Bu çözüm son derece verimsizdir: önce liste tek tek karakterlere ayrıştırılır, daha sonra orijinal dizideki her bir karakter için noktalama işareti kümesinin tamamı geçer, ardından karakterler geri birleştirilir ve sonra tekrar bölünür. Tüm bu "hareket", düzenli ifade tabanlı bir çözüme kıyasla çok karmaşıktır: belirli bir uygulamada hız önemli olmasa bile, karmaşık bir çözüme gerek yoktur. Yana remodül standarttır ve okunabilirliğini ve hız hem de verir o kaçınmış edilmelidir neden görmüyorum.
Eric O Lebigot

39

Uzman İpucu: Kullanım string.translate Python'un sahip olduğu en hızlı dize işlemleri için .

Bazı kanıtlar ...

İlk olarak, yavaş yol (pprzemek için üzgünüm):

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

Sonra, re.findall()(önerilen cevapta verildiği gibi) kullanıyoruz. Çok daha hızlı:

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

Son olarak şunu kullanıyoruz 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

Açıklama:

string.translateC ve Python birçok dize düzenleme işlevlerini aksine uygulanan, string.translate değil yeni bir dize üretirler. Dize değiştirme için alabileceğiniz kadar hızlı.

Yine de, bu büyüyü yapmak için bir çeviri tablosuna ihtiyaç duyduğu için biraz garip. maketrans()Kolaylık işleviyle bir çeviri tablosu yapabilirsiniz . Buradaki amaç, istenmeyen tüm karakterleri boşluklara çevirmektir. Bire bir ikame. Yine, yeni veri üretilmez. Yani bu hızlı !

Sonra, iyi eski kullanıyoruz split(). split()varsayılan olarak tüm boşluk karakterleri üzerinde çalışır ve bölme için birlikte gruplandırılır. Sonuç istediğiniz kelimelerin listesi olacaktır. Ve bu yaklaşım neredeyse 4 kat daha hızlı re.findall()!


4
Burada bir test yaptım ve unicode kullanmanız gerekiyorsa, patt = re.compile(ur'\w+', re.UNICODE); patt.findall(S)dönüştürme işleminden daha hızlı kullanmanız gerekir, çünkü dönüşüm uygulamadan önce dizeyi kodlamanız ve unicode'a geri dönmek için bölmeden sonra listedeki her öğenin kodunu çözmeniz gerekir.
Rafael S.Calsaverini

Tercüme uygulamasını tek satırlık yapabilir ve S'nin aşağıdakilerle ayırıcılar arasında olmadığından emin olabilirsiniz:s.translate(''.join([(chr(i) if chr(i) not in seps else seps[0]) for i in range(256)])).split(seps[0])
ocaklar

Hiçbiri alınmadı. Elma ve portakalları karşılaştırıyorsunuz. ;) Python 3'teki çözümüm hala çalışıyor; P ve çoklu karakter ayırıcılar için desteği var. :) yeni bir dize ayırmadan bunu basit bir şekilde yapmayı deneyin. :) ama doğru, benimki komut satırı parametrelerini ayrıştırmakla sınırlı, örneğin bir kitapla sınırlı değil.
pprzemek

"yeni bir dize üretmez" diyorsunuz, yani belirli bir dizede yerinde çalışıyor mu? Şimdi python 2.7 ile test ettim ve oroginal dize değiştirmiyor ve yenisini döndürüyor.
Prokop Hapala

26

Benzer bir ikilem vardı ve 're' modülünü kullanmak istemiyordum.

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
Bunu severim. Sadece bir not, ayırıcıların sırası önemlidir. Bu açıksa özür dilerim.
crizCraig

2
Neden rehem daha hızlı hem de daha açık olan modülü kullanmıyorsunuz (düzenli ifadeler özellikle net değil, ama çok daha kısa ve doğrudan olduğu için)?
Eric O Lebigot

13

İlk olarak, regex veya str.translate(...)tabanlı çözümlerin en iyi performans gösterdiğini başkalarıyla kabul etmek istiyorum . Kullanım durumum için bu işlevin performansı önemli değildi, bu yüzden bu ölçütlerle düşündüğüm fikirleri eklemek istedim.

Benim asıl amacım, diğer cevaplardan bazı fikirleri, sadece normal ifadelerden daha fazlasını içeren dizeler için çalışabilecek tek bir çözüme dönüştürmekti.

Herhangi bir yaklaşımda, string.punctuationmanuel olarak tanımlanmış bir liste yerine kullanmayı da düşünebilirsiniz .

Seçenek 1 - re.sub

Şimdiye kadar hiçbir cevap görmek için sürpriz oldu re.sub (...) kullanır . Bunu bu soruna basit ve doğal bir yaklaşım olarak görüyorum.

import re

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

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

Bu çözümde, çağrıyı iç içe re.sub(...)yerleştirdim re.split(...)- ancak performans kritikse, normal ifadeyi dışarıda derlemek faydalı olabilir - kullanım durumum için fark önemli değildi, bu yüzden basitlik ve okunabilirliği tercih ediyorum.

Seçenek 2 - str.replace

Bu birkaç satır daha, ancak regex'te belirli bir karakterden kaçmanız gerekip gerekmediğini kontrol etmek zorunda kalmadan genişletilebilir olma avantajına sahiptir.

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

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

words = my_str.split()

Bunun yerine str.replace'ı dizeye eşlemek güzel olurdu, ancak değişmez dizelerle yapılabileceğini düşünmüyorum ve bir karakter listesine karşı eşleme yaparken her karaktere karşı her yedek çalışacaktı kulağa aşırı geliyor. (Düzenle: İşlevsel bir örnek için bir sonraki seçeneğe bakın.)

Seçenek 3 - functools.reduce

(Python 2'de reduceglobal ad alanında functools'tan içe aktarılmadan kullanılabilir.)

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

Hm, başka bir yöntem kullanmaktır str.translate- unicode özellikli değildir, ancak diğer yöntemlerden daha hızlıdır ve bu nedenle bazı durumlarda iyi olabilir: replacements=',-!?'; import string; my_str = my_str.translate(string.maketrans(replacements, ' ' * len(replacements)))Ayrıca burada tuple veya liste.
MarSoft

@MarSoft Teşekkürler! Cevabın en üstünde birinden bahsettim, ancak mevcut cevaplar zaten iyi tartıştığı için eklememeye karar verdim.
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]

Sonra bu üç katmanlı olur:

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

açıklama

Haskell'de Liste monadı olarak bilinir. Monad'ın arkasındaki fikir, bir kez "monad'da" bir şey sizi dışarı çıkarana kadar monad'da kalmanızdır. Örneğin Haskell'de, python range(n) -> [1,2,...,n]işlevini bir Liste üzerinden eşlediğinizi varsayalım . Sonuç bir Liste ise, yerinde Liste'ye eklenecektir, böylece böyle bir şey elde edersiniz map(range, [3,4,1]) -> [0,1,2,0,1,2,3,0]. Bu, haritaya ekleme (veya haritaya ekleme veya bunun gibi bir şey) olarak bilinir. Buradaki fikir, uyguladığınız bu işlemi (bir jetona bölme) elde etmeniz ve bunu her yaptığınızda, sonucu listeye katmanızdır.

Bunu bir işleve soyutlayabilir ve tokens=string.punctuationvarsayılan olarak sahip olabilirsiniz.

Bu yaklaşımın avantajları:

  • Bu yaklaşım (naif normal ifade tabanlı yaklaşımlardan farklı olarak) keyfi uzunluklu belirteçlerle çalışabilir (bu normal ifade daha gelişmiş sözdizimiyle de yapılabilir).
  • Yalnızca jetonlarla sınırlı değilsiniz; her jetonun yerine rastgele bir mantığınız olabilir, örneğin "jetonlardan" biri, iç içe geçmiş parantezlerin ne olduğuna göre bölünen bir işlev olabilir.

Düzgün Haskell çözümü, ancak IMO bu Python'da mappend olmadan daha net yazılabilir.
Vlad the Impala

@ Goose: nokta, 2 satır işlevinin map_then_appendbir sorunu 2 astarlı yapmak için kullanılabileceği ve diğer birçok sorunun yazılmasının çok daha kolay olmasıydı . Diğer çözümlerin çoğunda repython olmayan normal ifade modülü kullanılır . Ama cevabımı gerçekten özlü olduğunda cevapsız ve şişman görünmesini nasıl yaptığımdan memnun kalmadım ... Düzenleyeceğim ...
ninjagecko

Bunun Python'da yazılı olarak çalışması mı gerekiyor? benim fragmentssonucum sadece dizgede (jetonlar dahil) karakterlerin bir listesidir.
Rick,

@RickTeachey: benim için hem python2 hem de python3'te çalışıyor.
ninjagecko

hmmmm. Belki de örnek biraz belirsizdir. Ben cevap kod denedi sahip dahil olmak üzere farklı yolu-her türlü fragments = ['the,string'], fragments = 'the,string'ya da fragments = list('the,string')ve bunların hiçbiri doğru çıkışını üretiyoruz.
Rick, Monica'yı

5

bunu dene:

import re

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

bu yazdırılacak ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']


4

İki kez değiştirin:

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

sonuç:

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

4

Ben yeniden seviyorum , ama iş onsuz benim çözüm:

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 .__ include__ 'in' operatörü tarafından kullanılan bir yöntemdir. Temelde aynı

lambda ch: ch in sep

ama burada daha uygun.

groupby dize ve fonksiyonumuzu alır. Dizeyi bu işlevi kullanarak gruplar halinde böler: bir işlev değeri her değiştiğinde - yeni bir grup oluşturulur. Yani, sep .__ içerir__ tam olarak ihtiyacımız olan şeydir.

groupby bir çift dizisi döndürür; burada [0] çifti fonksiyonumuzun bir sonucudur ve pair [1] bir gruptur. 'Değilse k' kullanarak , ayırıcılarla grupları filtreliyoruz (çünkü sep .__ içeren bir sonuç ayırıcılarda True'dur ). Eh, hepsi bu - şimdi her birinin bir kelime olduğu bir grup grubumuz var (grup aslında bir yinelenebilir, bu yüzden onu dizeye dönüştürmek için join kullanıyoruz ).

Bu çözüm oldukça geneldir, çünkü dizeyi ayırmak için bir işlev kullanır (ihtiyacınız olan herhangi bir koşula bölebilirsiniz). Ayrıca, ara dizeler / listeler oluşturmaz ( her grup bir yineleyici olduğundan birleştirmeyi kaldırabilirsiniz ve ifade tembelleşir)


4

Re.split re modülü işlevini kullanmak yerine pandaların series.str.split yöntemini kullanarak aynı sonucu elde edebilirsiniz.

İlk olarak, yukarıdaki dizeyle bir dizi oluşturun ve yöntemi seriye uygulayın.

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

parametresi pat , ayırıcıları alır ve bölünmüş dizeyi bir dizi olarak döndürür. Burada iki sınırlayıcı bir | (veya operatör). Çıktı aşağıdaki gibidir:

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


1
Bu bir ayrıntı meselesi değil, bir dizeyi bir panda serisine dönüştürdükten sonra basit bir görev gerçekleştirmek için tüm kütüphaneyi (sevdiğim, BTW) içe aktarma gerçeğidir. Pek "Occam dostu" değil.
zar3bski

3

Kendimi Python ile tanıyorum ve aynı şeye ihtiyacım vardı. Findall çözümü daha iyi olabilir, ancak bununla geldim:

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

Zekice, boşluğu olmayan bir em-dash hariç düşünebileceğim tüm İngilizce dilbilgisi yapıları üzerinde çalışmalıdır - bu, örneğin. (Geçici çözüm)
ninjagecko

3

maketrans ve translate kullanarak kolayca ve düzgünce yapabilirsiniz

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

Python
revliscano

3

Python 3'te PY4E - Herkes için Python yöntemini kullanabilirsiniz .

Biz dize yöntemleri kullanarak bu sorunları hem çözebilir lower, punctuationve translate. translateYöntemlerden en ince olduğunu. İşte dokümantasyon translate:

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

İçindeki karakterleri fromstr, aynı konumdaki karakterle değiştirin ve içindeki tostrtüm karakterleri silin deletestr. fromstrVe tostrboş dizisi şeklinde olabilir ve deletestrparametre ihmal edilebilir.

"Noktalama işaretlerini" görebilirsiniz:

In [10]: import string

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

Örneğiniz için:

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

Daha fazla bilgi için başvurabilirsiniz:


2
Dizelerin translate () ve maketrans () yöntemleri ilginçtir, ancak bu yöntem "ayırıcılara bölme" (veya boşluk) başarısız olur: örneğin, "büyük bir mağara girişi vardı" yerine "mağara" sözcüğü yanlış üretilir beklenen “mağara” ve “içeride”… Bu nedenle, sorunun sorusu bunu yapmaz.
Eric O Lebigot

@EricLebigot'un yorumladığı gibi. Yukarıdaki yöntem, sorunun çok iyi istediğini yapmaz.
Jeremy Anifacc

2

Bunu başarmanın başka bir yolu da Doğal Dil Araç Kiti'ni ( nltk ) kullanmaktır.

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

Bu yazdırır: ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Bu yöntemin en büyük dezavantajı nltk paketini yüklemeniz gerektiğidir .

Faydaları, belirteçlerinizi aldıktan sonra nltk paketinin geri kalanıyla çok eğlenceli şeyler yapabilmenizdir.


1

Her şeyden önce, niyetinizin noktalama işlevini ayırma işlevlerinde sınırlayıcı olarak kullanmak olduğunu düşünmüyorum. Açıklamanız, sonuçta elde edilen dizelerden noktalama işaretlerini kaldırmak istediğinizi gösterir.

Bu oldukça sık karşılaşıyorum ve her zamanki çözümüm yeniden gerektirmez.

Liste anlama özellikli tek astarlı lambda işlevi:

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


İşlev (geleneksel)

Geleneksel bir işlev olarak, bu hala liste kavrayışı olan sadece iki satırdır (buna ek olarak 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']

Ayrıca doğal olarak kasılmalar ve tireli kelimeler bozulmadan bırakılır. Her zaman text.replace("-", " ")tire işaretlerini bölmeden önce boşluklara dönüştürmek için kullanabilirsiniz .

Lambda veya Liste Kavrama olmadan Genel İşlev

Daha genel bir çözüm için (ortadan kaldıracağınız karakterleri belirtebileceğiniz) ve bir liste anlama olmadan şunları elde edersiniz:

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

Tabii ki, lambda işlevini her zaman belirli bir karakter dizisine de genelleştirebilirsiniz.


1

Her şeyden önce, normal işlemden daha hızlı çalıştığı için bir döngüde herhangi bir RegEx işlemi gerçekleştirmeden önce daima re.compile () öğesini kullanın.

bu yüzden probleminiz için önce deseni derleyin ve daha sonra üzerinde işlem yapın.

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

1

İşte bazı açıklama ile cevap.

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'

veya bir satırda, bunu yapabiliriz:

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

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

güncellenmiş cevap


1

Girilen iki dizeyi (bölünecek kaynak dize ve ayırıcıların bölünmüş liste dizesi) alan ve bölünmüş sözcüklerin bir listesini çıkaran bir işlev oluşturun:

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

Pprzemek'in çözümünü seviyorum çünkü sınırlayıcıların tek karakterler olduğunu varsaymıyor ve bir regex'den (ayırıcıların sayısı çok uzun sürerse iyi sonuç vermeyecek) yararlanmaya çalışmaz.

Açıklık için yukarıdaki çözümün daha okunabilir bir versiyonu:

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

@ooboo ile aynı sorunu var ve bu konuyu bulmak @ ghostdog74 bana ilham verdi, belki birisi benim çözümüm faydalı bulur

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

boşluk yerine bir şey girin ve boşluklarda bölmek istemiyorsanız aynı karakteri kullanarak bölün.


kelimeyi kullanarak bölmem gerekirse ne olur?
Harsha Biyani

0

İşte benim birden fazla sınırlayıcı ile bir bölünme gitmek:

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

Aşağıdaki ihtiyaçlarınızı karşılamak için en iyi cevap olduğunu düşünüyorum:

\W+ bu durum için uygun olabilir, ancak diğer durumlar için uygun olmayabilir.

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

Katılıyorum, \wve \Wçözümler sorunun (başlığı) bir cevap değildir. Cevabınızda, |kaldırılması gerektiğini unutmayın ( expr0|expr1yerine bunun yerine düşünüyorsunuz [char0 char1…]). Ayrıca, compile()düzenli ifadeye gerek yoktur .
Eric O Lebigot

0

Heres benim almak ....

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

replace()En iyi yolu seviyorum . Aşağıdaki yordam, bir dizede tanımlanan tüm ayırıcıları splitlistiçindeki ilk ayırıcıya değiştirir splitlistve ardından metni bir ayırıcıya ayırır. Ayrıca splitlist, boş bir dize olup olmadığını da açıklar . İçinde boş dize olmayan bir kelime listesi döndürür.

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

İşte kullanımı:

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

0

Tersinir bir işlem istiyorsanız (ayırıcıları koruyun), bu işlevi kullanabilirsiniz:

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

Son zamanlarda bunu yapmak gerekiyordu ama biraz standart kütüphane str.splitfonksiyonu ile eşleşen bir fonksiyon istedim , bu fonksiyon 0 veya 1 argüman ile çağrıldığında standart kütüphane ile aynı davranır.

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

NOT : Bu işlev yalnızca ayırıcılarınız tek bir karakterden (benim kullanıcı tabanım gibi) oluştuğunda yararlıdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.