Python'daki bir dizeden alfasayısal karakterlerden başka her şeyi soyma


339

Python kullanarak alfasayısal olmayan tüm karakterleri bir dizeden ayırmanın en iyi yolu nedir?

Bu sorunun PHP varyantında sunulan çözümler muhtemelen bazı küçük ayarlarla çalışacaktır, ancak bana çok 'pitonik' görünmüyor.

Kayıt için, sadece dönemleri ve virgülleri (ve diğer noktalama işaretlerini) değil, aynı zamanda tırnak işaretleri, köşeli parantezler vb.


8
'Æøå', 'مرحبا', 'สวัสดี', 'こ ん に ち は' gibi uluslararası alfasayısal karakterlerle ilgileniyor musunuz?
Pimin Konstantin Kefaloukos

4
@PiminKonstantinKefaloukos Evet Uluslararası karakterleri önemsiyorum, bu nedenle re.UNICODE'u kullanmak için kabul edilen cevap hakkındaki yorumum.
Mark van Lent

Yanıtlar:


337

Bazı işlevleri meraktan zamanladım. Bu testlerde alfasayısal olmayan karakterleri dizeden kaldırıyorum string.printable(yerleşik stringmodülün bir parçası ). Kullanımı derlendi '[\W_]+'ve pattern.sub('', str)en hızlı olduğu bulundu.

$ python -m timeit -s \
     "import string" \
     "''.join(ch for ch in string.printable if ch.isalnum())" 
10000 loops, best of 3: 57.6 usec per loop

$ python -m timeit -s \
    "import string" \
    "filter(str.isalnum, string.printable)"                 
10000 loops, best of 3: 37.9 usec per loop

$ python -m timeit -s \
    "import re, string" \
    "re.sub('[\W_]', '', string.printable)"
10000 loops, best of 3: 27.5 usec per loop

$ python -m timeit -s \
    "import re, string" \
    "re.sub('[\W_]+', '', string.printable)"                
100000 loops, best of 3: 15 usec per loop

$ python -m timeit -s \
    "import re, string; pattern = re.compile('[\W_]+')" \
    "pattern.sub('', string.printable)" 
100000 loops, best of 3: 11.2 usec per loop

2
Çok ilginç sonuçlar: Normal ifadelerin daha yavaş olmasını beklerdim. İlginçtir, bu bir başka seçeneği ile (çalıştı valid_characters = string.ascii_letters + string.digitsizledi join(ch for ch in string.printable if ch in valid_characters)ve daha hızlı 6'dan mikrosaniye oldu isalnum().. Seçeneğiyle Hala çok daha yavaş olsa regexp daha
DRAL

+1, ölçüm süresi iyidir! (ancak sondan bir önceki durumda, pattern.sub('', string.printable)bunun yerine - bir RE nesneniz olduğunda re.sub'ı çağırmak için aptalca yapın! -).
Alex Martelli

46
Kayıt için: re.compile('[\W_]+', re.UNICODE)unicode'u güvenli hale getirmek için kullanın .
Mark van Lent

3
beyaz alanı çıkarmadan nasıl yapıyorsun?
maudulus

6
boşluk bırakmadan yap: re.sub ('[\ W _] +', '', cümle, bayraklar = re.UNICODE)
PALEN

269

Kurtarmaya düzenli ifadeler:

import re
re.sub(r'\W+', '', your_string)

Python Tanım olarak '\W== [^a-zA-Z0-9_], dışlayan tüm numbers, lettersve_


2
Regexp'de artı işareti ne yapar? (Bunun ne anlama geldiğini biliyorum, sadece re.sub için neden gerekli olduğunu merak ediyorum.)
Mark van Lent

7
@ Mark: ikame hızını tek tek kaldırmak yerine tek seferde bir bloktaki tüm kelime olmayan karakterlerden kurtulacağı için değiştirmeyi hızlandıracağını hayal ediyorum.
DrAl

2
Evet, bir süre önce performans açısından kritik bazı kodlar ayarlarken bunu sıraladım. Hızlandırmak için önemli karakter açıklıkları varsa hızlandırma çok büyük.
Karıncalar Aasma

20
Bu durumda uygun olmayabilir, ancak \Walt çizgileri de tutacaktır.
Blixt

12
@Blixt ipucundan sonra, yalnızca harf ve rakam istiyorsanız re.sub (r '[^ a-zA-Z0-9]', '', your_string) yapabilirsiniz
Nigini

69

Str.translate () yöntemini kullanın .

Bunu sık sık yapacağınızı varsayarsak:

(1) Bir kez, silmek istediğiniz tüm karakterleri içeren bir dize oluşturun:

delchars = ''.join(c for c in map(chr, range(256)) if not c.isalnum())

(2) Bir dizeyi her ne zaman kırmak isterseniz:

scrunched = s.translate(None, delchars)

Kurulum maliyeti muhtemelen yeniden derleme ile karşılaştırılır; marjinal maliyet çok daha düşüktür:

C:\junk>\python26\python -mtimeit -s"import string;d=''.join(c for c in map(chr,range(256)) if not c.isalnum());s=string.printable" "s.translate(None,d)"
100000 loops, best of 3: 2.04 usec per loop

C:\junk>\python26\python -mtimeit -s"import re,string;s=string.printable;r=re.compile(r'[\W_]+')" "r.sub('',s)"
100000 loops, best of 3: 7.34 usec per loop

Not: Karşılaştırma verileri olarak string.printable kullanılması, '[\ W _] +' desenine haksız avantaj sağlar ; alfasayısal olmayan tüm karakterler tek bir gruptadır ... tipik verilerde birden fazla değişiklik yapılabilir:

C:\junk>\python26\python -c "import string; s = string.printable; print len(s),repr(s)"
100 '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'

Re.sub'a biraz daha fazla iş verirseniz ne olur:

C:\junk>\python26\python -mtimeit -s"d=''.join(c for c in map(chr,range(256)) if not c.isalnum());s='foo-'*25" "s.translate(None,d)"
1000000 loops, best of 3: 1.97 usec per loop

C:\junk>\python26\python -mtimeit -s"import re;s='foo-'*25;r=re.compile(r'[\W_]+')" "r.sub('',s)"
10000 loops, best of 3: 26.4 usec per loop

1
Çeviri kullanmak gerçekten biraz daha hızlı. Değiştirme / çeviri yapmadan hemen önce bir for döngüsü eklerken bile (kurulum maliyetlerinin daha düşük ağırlığa sahip olmasını sağlamak için), çeviriyi makinemdeki normal ifadeden yaklaşık 17 kat daha hızlı hale getirir. Bunu bildiğim iyi oldu.
Mark van Lent

3
Bu kesinlikle en pitonik çözümdür.
codygman

1
Bu neredeyse beni ikna ediyor, ama bunun string.punctuationyerine kullanmanızı öneririm''.join(c for c in map(chr, range(256)) if not c.isalnum())
ArnauOrriols

1
Bunun strnesneler için çalıştığını, ancak unicodenesneler için çalışmadığını unutmayın .
Yavar

@John Machin Bu aslında bir argüman olarak aktarılan bir liste kavraması .join()mı?
AdjunctProfessorFalcon

42

Deneyebilirsiniz:

print ''.join(ch for ch in some_string if ch.isalnum())

16
>>> import re
>>> string = "Kl13@£$%[};'\""
>>> pattern = re.compile('\W')
>>> string = re.sub(pattern, '', string)
>>> print string
Kl13

1
Cevabınızı sevdim ama Arapça karakterleri de siler, nasıl saklayacağınızı söyleyebilir misiniz
Charif DZ

14

Nasıl olur:

def ExtractAlphanumeric(InputString):
    from string import ascii_letters, digits
    return "".join([ch for ch in InputString if ch in (ascii_letters + digits)])

Bu InputString, birleştirilmiş ascii_lettersve digitsdizelerde mevcut olmaları durumunda karakterlerin bir listesini üretmek için liste kavrama özelliğini kullanarak çalışır . Daha sonra listeyi bir dizeye birleştirir.


String.ascii_letters sayıları değil, sadece harfleri (duh) içeriyor gibi görünüyor. Ayrıca sayılara ihtiyacım var ...
Mark van Lent

String.digits eklemek gerçekten bahsettiğim sorunu çözecektir. :)
Mark van Lent

Evet, sorunuzu okumak için geri döndüğümde fark ettim. Kendine not: okumayı öğren!
DrAl

5

Buradaki diğer cevaplardan bir kaçış olarak, bir dizenin içeriğini sınırlamak istediğiniz bir dizi karakteri tanımlamak için gerçekten basit ve esnek bir yol sunuyoruz. Bu durumda, alfasayısal PLUS tire ve alt çizgisine izin veriyorum. PERMITTED_CHARSKullanım durumunuza uygun şekilde karakterleri ekleyin veya çıkarın .

PERMITTED_CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-" 
someString = "".join(c for c in someString if c in PERMITTED_CHARS)

3
Hafif hatalara eğilimli izin verilen karakterleri kodlamak yerine kullanın string.digits + string.ascii_letters + '_-'.
Reti43

Öneriniz yanlış değil, ancak hedefiniz bu ise "yazmanın" pek çok karakterini de kaydetmiyor. Yazımı kopyalarsanız, yazım hatası da alamazsınız! Ancak cevabımın asıl noktası, hangi karakterlere tam olarak izin vermek istediğinizi tanımlamak için açık, açık uçlu ve basit bir araca izin vermektir.
BuvinJ

Orta yol olarak, bu önerileri birleştirebilir SPECIAL_CHARS = '_-'ve kullanabilirsinizstring.digits + string.ascii_letters + SPECIAL_CHARS
BuvinJ

Kod golf yapıyoruz sürece makul ne olduğunu bir öneri oldu. 52 alfabe harfini sırayla yazmak için klavyenin etrafında dolaşmak, bir veya iki nesneyi kullanmak için bir paketi içe aktarmaktan çok daha uzun sürer. Ve bu, hepsini doğru yazdığınızı iki kez kontrol etme süresini içermez. İyi uygulamalarla ilgili, hepsi bu.
Reti43

Seni duyuyorum! Buradaki asıl nokta, karakter kümenizle daha spesifik olmak istemeniz durumunda aşırı esneklik.
BuvinJ

5
sent = "".join(e for e in sent if e.isalpha())

Açıklamaya çalışacağım: tüm karakter karakterleri e for e in sentüzerinden if e.isalpha()geçer ve geçerli karakter alfabetik sembol olup olmadığını ifade yoluyla kontrol eder , eğer öyleyse - sentdeğişkene katılır sent = "".join()ve tüm alfabetik olmayan semboller ""(boş dize) ile değiştirilir çünkü bir joinfonksiyonu.
Sysanin

C regex'e güvenmek yerine karakter başına bir döngü yaptığı için bu son derece yavaş değil mi?
dcsan

3
for char in my_string:
    if not char.isalnum():
        my_string = my_string.replace(char,"")

2

ASCII yazdırılabilir rasgele dizeleriyle zamanlama:

from inspect import getsource
from random import sample
import re
from string import printable
from timeit import timeit

pattern_single = re.compile(r'[\W]')
pattern_repeat = re.compile(r'[\W]+')
translation_tb = str.maketrans('', '', ''.join(c for c in map(chr, range(256)) if not c.isalnum()))


def generate_test_string(length):
    return ''.join(sample(printable, length))


def main():
    for i in range(0, 60, 10):
        for test in [
            lambda: ''.join(c for c in generate_test_string(i) if c.isalnum()),
            lambda: ''.join(filter(str.isalnum, generate_test_string(i))),
            lambda: re.sub(r'[\W]', '', generate_test_string(i)),
            lambda: re.sub(r'[\W]+', '', generate_test_string(i)),
            lambda: pattern_single.sub('', generate_test_string(i)),
            lambda: pattern_repeat.sub('', generate_test_string(i)),
            lambda: generate_test_string(i).translate(translation_tb),

        ]:
            print(timeit(test), i, getsource(test).lstrip('            lambda: ').rstrip(',\n'), sep='\t')


if __name__ == '__main__':
    main()

Sonuç (Python 3.7):

       Time       Length                           Code                           
6.3716264850008880  00  ''.join(c for c in generate_test_string(i) if c.isalnum())
5.7285426190064750  00  ''.join(filter(str.isalnum, generate_test_string(i)))
8.1875841680011940  00  re.sub(r'[\W]', '', generate_test_string(i))
8.0002205439959650  00  re.sub(r'[\W]+', '', generate_test_string(i))
5.5290945199958510  00  pattern_single.sub('', generate_test_string(i))
5.4417179649972240  00  pattern_repeat.sub('', generate_test_string(i))
4.6772285089973590  00  generate_test_string(i).translate(translation_tb)
23.574712151996210  10  ''.join(c for c in generate_test_string(i) if c.isalnum())
22.829975890002970  10  ''.join(filter(str.isalnum, generate_test_string(i)))
27.210196289997840  10  re.sub(r'[\W]', '', generate_test_string(i))
27.203713296003116  10  re.sub(r'[\W]+', '', generate_test_string(i))
24.008979928999906  10  pattern_single.sub('', generate_test_string(i))
23.945240008994006  10  pattern_repeat.sub('', generate_test_string(i))
21.830899796994345  10  generate_test_string(i).translate(translation_tb)
38.731336012999236  20  ''.join(c for c in generate_test_string(i) if c.isalnum())
37.942474347000825  20  ''.join(filter(str.isalnum, generate_test_string(i)))
42.169366310001350  20  re.sub(r'[\W]', '', generate_test_string(i))
41.933375883003464  20  re.sub(r'[\W]+', '', generate_test_string(i))
38.899814646996674  20  pattern_single.sub('', generate_test_string(i))
38.636144253003295  20  pattern_repeat.sub('', generate_test_string(i))
36.201238164998360  20  generate_test_string(i).translate(translation_tb)
49.377356811004574  30  ''.join(c for c in generate_test_string(i) if c.isalnum())
48.408927293996385  30  ''.join(filter(str.isalnum, generate_test_string(i)))
53.901889764994850  30  re.sub(r'[\W]', '', generate_test_string(i))
52.130339455994545  30  re.sub(r'[\W]+', '', generate_test_string(i))
50.061149017004940  30  pattern_single.sub('', generate_test_string(i))
49.366573111998150  30  pattern_repeat.sub('', generate_test_string(i))
46.649754120997386  30  generate_test_string(i).translate(translation_tb)
63.107938601999194  40  ''.join(c for c in generate_test_string(i) if c.isalnum())
65.116287978999030  40  ''.join(filter(str.isalnum, generate_test_string(i)))
71.477421126997800  40  re.sub(r'[\W]', '', generate_test_string(i))
66.027950693998720  40  re.sub(r'[\W]+', '', generate_test_string(i))
63.315361931003280  40  pattern_single.sub('', generate_test_string(i))
62.342320287003530  40  pattern_repeat.sub('', generate_test_string(i))
58.249303059004890  40  generate_test_string(i).translate(translation_tb)
73.810345625002810  50  ''.join(c for c in generate_test_string(i) if c.isalnum())
72.593953348005020  50  ''.join(filter(str.isalnum, generate_test_string(i)))
76.048324580995540  50  re.sub(r'[\W]', '', generate_test_string(i))
75.106637657001560  50  re.sub(r'[\W]+', '', generate_test_string(i))
74.681338128997600  50  pattern_single.sub('', generate_test_string(i))
72.430461594005460  50  pattern_repeat.sub('', generate_test_string(i))
69.394243567003290  50  generate_test_string(i).translate(translation_tb)

str.maketrans& str.translateen hızlıdır, ancak ASCII olmayan tüm karakterleri içerir. re.compile& pattern.subdaha yavaştır, ancak bir şekilde ''.join&' den daha hızlıdır filter.


-1

Eğer doğru anladım en kolay yolu size esneklik bir sürü sağlar gibi düzenli ifade kullanmaktır ama diğer basit yöntem döngü aşağıdaki için kullanmaktır örnek ile kod da kelime oluşumunu sayılan ve sözlükte saklanır ..

s = """An... essay is, generally, a piece of writing that gives the author's own 
argument — but the definition is vague, 
overlapping with those of a paper, an article, a pamphlet, and a short story. Essays 
have traditionally been 
sub-classified as formal and informal. Formal essays are characterized by "serious 
purpose, dignity, logical 
organization, length," whereas the informal essay is characterized by "the personal 
element (self-revelation, 
individual tastes and experiences, confidential manner), humor, graceful style, 
rambling structure, unconventionality 
or novelty of theme," etc.[1]"""

d = {}      # creating empty dic      
words = s.split() # spliting string and stroing in list
for word in words:
    new_word = ''
    for c in word:
        if c.isalnum(): # checking if indiviual chr is alphanumeric or not
            new_word = new_word + c
    print(new_word, end=' ')
    # if new_word not in d:
    #     d[new_word] = 1
    # else:
    #     d[new_word] = d[new_word] +1
print(d)

bu cevap faydalıysa lütfen bunu değerlendirin!

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.