Python kullanarak dizeden basamaklar dışındaki karakterler kaldırılsın mı?


137

Dizeden sayılar dışındaki tüm karakterleri nasıl kaldırabilirim?


@ Jan Tojnar: Bir örnek verebilir misiniz?
João Silva

@JG: Ben gtk.Entry () var ve içine çarpma şamandıra girmesini istiyorum.
Jan Tojnar

1
@JanTojnar yanıt ikiye göre re.sub yöntemini kullanır ve hangi karakterleri saklayacağını açık bir şekilde listeler, örneğin re.sub ("[^ 0123456789 \.]", "", "Poo123.4and5fish")
Roger Heathcote

Yanıtlar:


112

Python 2. * 'de, en hızlı yaklaşım .translateyöntemdir:

>>> x='aaa12333bb445bb54b5b52'
>>> import string
>>> all=string.maketrans('','')
>>> nodigs=all.translate(all, string.digits)
>>> x.translate(all, nodigs)
'1233344554552'
>>> 

string.maketransbu durumda aynı olan bir çeviri tablosu (uzunluk 256 dizesi) yapar ''.join(chr(x) for x in range(256))(sadece ;-) daha hızlıdır). .translateçeviri tablosunu uygular (burada allözdeşlik anlamına geldiği için anlamsızdır ) VE ikinci argümanda bulunan karakterleri siler - anahtar kısım.

.translateçok farklı Unicode dizeleri çalışır (Python 3 ve dizeleri - ben yapmak dileği soruları Python büyük salınımlı ilgi olduğu belirtilmedi!) - oldukça bu basit, oldukça bu hızlı olsa hala oldukça kullanışlı.

Geri 2. *, performans farkı etkileyici ...:

$ python -mtimeit -s'import string; all=string.maketrans("", ""); nodig=all.translate(all, string.digits); x="aaa12333bb445bb54b5b52"' 'x.translate(all, nodig)'
1000000 loops, best of 3: 1.04 usec per loop
$ python -mtimeit -s'import re;  x="aaa12333bb445bb54b5b52"' 're.sub(r"\D", "", x)'
100000 loops, best of 3: 7.9 usec per loop

İşleri 7-8 kat hızlandırmak neredeyse yer fıstığı değildir, bu nedenle translateyöntem bilmeye ve kullanmaya değer. Diğer popüler RE olmayan yaklaşım ...:

$ python -mtimeit -s'x="aaa12333bb445bb54b5b52"' '"".join(i for i in x if i.isdigit())'
100000 loops, best of 3: 11.5 usec per loop

RE'den% 50 daha yavaştır, bu nedenle .translateyaklaşım onu ​​bir büyüklük sırasına göre yener.

Python 3'te veya Unicode için, silmek istediğiniz şey için .translatedöndürülen bir eşleme (sıralı karakterlerle, doğrudan karakterlerle değil, anahtar olarak) geçirmeniz gerekir None. Birkaç karakteri "her şeyden" silmek için bunu ifade etmenin uygun bir yolu:

import string

class Del:
  def __init__(self, keep=string.digits):
    self.comp = dict((ord(c),c) for c in keep)
  def __getitem__(self, k):
    return self.comp.get(k)

DD = Del()

x='aaa12333bb445bb54b5b52'
x.translate(DD)

ayrıca yayar '1233344554552'. Ancak, bunu xx.py içine koyarak ...:

$ python3.1 -mtimeit -s'import re;  x="aaa12333bb445bb54b5b52"' 're.sub(r"\D", "", x)'
100000 loops, best of 3: 8.43 usec per loop
$ python3.1 -mtimeit -s'import xx; x="aaa12333bb445bb54b5b52"' 'x.translate(xx.DD)'
10000 loops, best of 3: 24.3 usec per loop

... bu tür bir "silme" görevi için performans avantajının kaybolduğunu ve performans düşüşüne dönüştüğünü gösterir.


1
@ sunqiang, evet, kesinlikle - Py3k'nin metin dizesi türü olarak Unicode'a gitmesinin bir nedeni var, Py2'deki gibi bayt dizeleri yerine - Java ve C # her zaman aynı "dize unicode" meme ... Bazı tepegöz, belki, ama İngilizce dışında bir şey için çok daha iyi destek! -).
Alex Martelli

29
x.translate(None, string.digits)aslında sonuçlanan 'aaabbbbbb'şeyin tam tersidir.
Tom Dalling

4
Tom Dalling'in yorumlarını yansıtan ilk örneğiniz istenmeyen tüm karakterleri tutar - söylediklerinizin tam tersini yapar.
Chris Johnson

3
@ RyanB.Lynch ve arkadaşları, hata daha sonra bir editör ve söz konusu düzenlemeyi onaylayan diğer iki kullanıcı ile yapıldı , ki bu aslında tamamen yanlış. Geri alındı.
Nick T

1
yerleşimi geçersiz allkılıyor ... bundan emin değilim!
Andy Hayden

197

Şöyle kullanın re.sub:

>>> import re
>>> re.sub('\D', '', 'aas30dsa20')
'3020'

\D herhangi bir rakam olmayan karakterle eşleşir, bu nedenle yukarıdaki kod, esasen boş dize için rakam olmayan her karakterin yerini alır.

Veya şu şekilde kullanabilirsiniz filter(Python 2'de):

>>> filter(str.isdigit, 'aas30dsa20')
'3020'

Python 3'te, filtera yerine yineleyici döndürdüğünden list, aşağıdakini kullanabilirsiniz:

>>> ''.join(filter(str.isdigit, 'aas30dsa20'))
'3020'

Böyle basit bir görevde kötülük, ikincisi bence en iyisi, çünkü 'is ...' yöntemleri dizeler için en hızlı.
f0b0s

filtre örneğiniz py2k ile sınırlıdır
SilentGhost

2
@ f0b0s-iu9-info: zamanladınız mı? makinemde (py3k) yeniden filtre ile iki kat daha hızlı isdigit, jeneratör ile isdigtaralarında yarı yarıya
SilentGhost

@SilentGhost: Teşekkürler, py2k IDLE kullanıyordum. Şimdi düzeltildi.
João Silva

1
@asmaier rHam dize için kullanın :re.sub(r"\D+", "", "aas30dsa20")
Mitch McMabers

64
s=''.join(i for i in s if i.isdigit())

Başka bir jeneratör çeşidi.


Öldürdü ... + 1 Lamda kullanılsaydı daha iyi olurdu
Barath Ravikumar

17

Filtre kullanabilirsiniz:

filter(lambda x: x.isdigit(), "dasdasd2313dsa")

Python3.0'da buna katılmak zorundasınız (biraz çirkin :()

''.join(filter(lambda x: x.isdigit(), "dasdasd2313dsa"))

sadece py2k, py3k bir jeneratör döndürür
SilentGhost

dönüştürmek striçin listemin py2 ve PY3 hem çalışır hale getirmek için:''.join(filter(lambda x: x.isdigit(), list("dasdasd2313dsa")))
Luiz C.

13

bayer'in cevabı boyunca:

''.join(i for i in s if i.isdigit())

Hayır, bu -bir rakam olmadığından negatif sayılar için işe yaramaz .
Oli

12

Regex'i kullanarak kolayca yapabilirsiniz

>>> import re
>>> re.sub("\D","","£70,000")
70000

En kolay yol
Iorek

5
Bu, João Silva'nın 7 yıl önce verilen cevabından ne farkı var?
jww

7
x.translate(None, string.digits)

dizeden tüm basamakları siler. Harfleri silmek ve rakamları saklamak için şunları yapın:

x.translate(None, string.letters)

3
A TypeError: translate () tam olarak bir argüman alır (2 tane verilir). Bu sorunun şu anki durumunda neden kaldırıldığı oldukça sinir bozucu.
Bobort

çeviri python 2'den 3'e değiştirildi. python 3'te bu yöntemi kullanan sözdizimi x.translate (str.maketrans ('', '', string.digits)) ve x.translate (str.maketrans ('', '') , string.ascii_letters)). Bunların hiçbiri boşluk bırakmaz. Artık bu yaklaşımı gerçekten tavsiye etmem ...
ZaxR

5

Operasyon, yorumlarda ondalık basamak tutmak istediğinden bahsediyor. Bu, saklanacak karakterleri açıkça listeleyerek re.sub yöntemiyle (ikinci ve IMHO en iyi yanıta göre) yapılabilir.

>>> re.sub("[^0123456789\.]","","poo123.4and5fish")
'123.45'

"Poo123.4and.5fish" ne olacak?
Jan Tojnar

Kodumda, giriş dizesindeki nokta sayısını kontrol edip 1'den fazla ise bir hata yükseltirim
Roger Heathcote

4

Python 3 için hızlı bir sürüm:

# xx3.py
from collections import defaultdict
import string
_NoneType = type(None)

def keeper(keep):
    table = defaultdict(_NoneType)
    table.update({ord(c): c for c in keep})
    return table

digit_keeper = keeper(string.digits)

İşte regex ile performans karşılaştırması:

$ python3.3 -mtimeit -s'import xx3; x="aaa12333bb445bb54b5b52"' 'x.translate(xx3.digit_keeper)'
1000000 loops, best of 3: 1.02 usec per loop
$ python3.3 -mtimeit -s'import re; r = re.compile(r"\D"); x="aaa12333bb445bb54b5b52"' 'r.sub("", x)'
100000 loops, best of 3: 3.43 usec per loop

Bu yüzden benim için normal ifadeden 3 kat biraz daha hızlı. Ayrıca class Delyukarıdakinden daha hızlıdır , çünkü defaultdict(yavaş) Python yerine tüm aramalarını C'de yapar. Karşılaştırma için aynı sistemimdeki sürüm.

$ python3.3 -mtimeit -s'import xx; x="aaa12333bb445bb54b5b52"' 'x.translate(xx.DD)'
100000 loops, best of 3: 13.6 usec per loop

3

Bir jeneratör ifadesi kullanın:

>>> s = "foo200bar"
>>> new_s = "".join(i for i in s if i in "0123456789")

Bunun yerine''.join(n for n in foo if n.isdigit())
shxfee

2

Çirkin ama çalışıyor:

>>> s
'aaa12333bb445bb54b5b52'
>>> a = ''.join(filter(lambda x : x.isdigit(), s))
>>> a
'1233344554552'
>>>

neden yaparsınız list(s)?
SilentGhost

@SilentGhost bu benim yanlış anlama. teşekkür düzeltti vardı :)
Gant

Aslında, bu yöntemle, "birleştirme" yi kullanmanız gerektiğini düşünmüyorum. filter(lambda x: x.isdigit(), s)benim için iyi çalıştı. ... oh, çünkü Python 2.7 kullanıyorum.
Bobort

1
$ python -mtimeit -s'import re;  x="aaa12333bb445bb54b5b52"' 're.sub(r"\D", "", x)'

100000 döngü, döngü başına en iyi 3: 2,48 usec

$ python -mtimeit -s'import re; x="aaa12333bab445bb54b5b52"' '"".join(re.findall("[a-z]+",x))'

100000 döngü, döngü başına en iyi 3: 2,02 usec

$ python -mtimeit -s'import re;  x="aaa12333bb445bb54b5b52"' 're.sub(r"\D", "", x)'

100000 döngü, döngü başına en iyi 3: 2,37 usec

$ python -mtimeit -s'import re; x="aaa12333bab445bb54b5b52"' '"".join(re.findall("[a-z]+",x))'

100000 döngü, döngü başına en iyi 3: 1,97 usec

Birleşmenin alttan daha hızlı olduğunu gözlemlemiştim.


Neden iki yöntemi iki kez tekrarlıyorsunuz? Cevabınızın kabul edilen yanıttan ne kadar farklı olduğunu açıklayabilir misiniz?
Jan Tojnar

Her iki sonuç da aynı çıktıyı verir. Ancak, sadece sonuçların alt yöntemine katılmanın daha hızlı olduğunu göstermek istiyorum.
AnilReddy

Yapmazlar, kodunuz tam tersini yapar. Ayrıca dört ölçümünüz var ancak sadece iki yöntem var.
Jan Tojnar

1

Her karakteri okuyabilirsiniz. Rakamsa, cevaba ekleyin. str.isdigit() Yöntem bir karakter basamaklı olup olmadığını bilmek için bir yoldur.

your_input = '12kjkh2nnk34l34'
your_output = ''.join(c for c in your_input if c.isdigit())
print(your_output) # '1223434'

bu f0b0s'ın cevabından nasıl farklı?
Getirmek

0

Tek bir astar değil, çok basit:

buffer = ""
some_str = "aas30dsa20"

for char in some_str:
    if not char.isdigit():
        buffer += char

print( buffer )

0

Bunu kullandım. 'letters'kurtulmak istediğiniz tüm harfleri içermelidir:

Output = Input.translate({ord(i): None for i in 'letters'}))

Misal:

Input = "I would like 20 dollars for that suit" Output = Input.translate({ord(i): None for i in 'abcdefghijklmnopqrstuvwxzy'})) print(Output)

Çıktı: 20

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.