Видалити символи, крім цифр, із рядка за допомогою Python?


137

Як я можу видалити всі символи, крім цифр, із рядка?


@Jan Tojnar: Чи можете ви навести приклад?
Жоао Сільва

@JG: У мене є gtk.Entry (), і я хочу, щоб у нього ввійшов помножений плавець.
Ян Тойнар

1
@JanTojnar використовує метод re.sub відповідно до другої відповіді та чітко перераховує, які символи потрібно зберегти, наприклад re.sub ("[^ 0123456789 \.]", "", "Poo123.4and5fish")
Роджер Хіткот

Відповіді:


112

У Python 2. * на сьогоднішній день найшвидшим підходом є .translateметод:

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

string.maketransскладає таблицю перекладу (рядок довжиною 256), яка в цьому випадку така сама, як ''.join(chr(x) for x in range(256))(просто швидше зробити ;-). .translateзастосовує таблицю перекладу (що тут не має значення, оскільки allпо суті означає ідентичність) І видаляє символи, присутні у другому аргументі - ключовій частині.

.translateпрацює над різними рядками Unicode (і рядки в Python 3 - я хочу , щоб питання, які основні версії Python цікаві!) - не зовсім прості, не дуже швидкі, хоча все ще досить зручні.

Повернення до 2. *, різниця в продуктивності вражає ...:

$ 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

Перевищення речей в 7-8 разів навряд чи арахіс, тому translateметод варто знати і використовувати. Інший популярний підхід, який не стосується РЕ ...:

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

на 50% повільніше, ніж РЕ, тому .translateпідхід долає його на порядок більше.

У Python 3 або Unicode вам потрібно передати .translateвідображення (з порядками, а не символами безпосередньо, як ключі), яке повертає Noneте, що ви хочете видалити. Ось зручний спосіб висловити це для видалення кількох символів "все, окрім":

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)

також випромінює '1233344554552'. Однак, помістивши це в xx.py, ми маємо ...:

$ 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

... що показує, що для цього виду завдань "видалення" зникає перевага в продуктивності і зменшується продуктивність.


1
@sunqiang, так, абсолютно - є причина, чому Py3k перейшов до Unicode як тип текстового рядка, замість байтових рядків, як у Py2 - та сама причина, що Java та C # завжди мали однаковий мем "string означає unicode" ... Деякі накладні, можливо, але набагато краща підтримка майже нічого, крім англійської! -).
Алекс Мартеллі

29
x.translate(None, string.digits)насправді це призводить до 'aaabbbbbb', що є протилежним тому, що призначено.
Том Даллінг

4
Повторюючи коментарі Тома Даллінга, ваш перший приклад зберігає всіх небажаних персонажів - робить протилежне тому, що ви сказали.
Кріс Джонсон

3
@ RyanB.Lynch та ін, помилка була з пізнішим редактором та двома іншими користувачами, які схвалили вказане редагування , що, власне, є абсолютно неправильним. Повернено.
Нік Т

1
переважаючий allвбудований ... не впевнений у цьому!
Енді Хайден

197

Використовуйте re.subтак:

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

\D відповідає будь-якому нецифровому символу, тому, наведений вище код, по суті замінює кожен нецифровий символ для порожнього рядка.

Або ви можете використовувати filter, як так (у Python 2):

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

Оскільки в Python 3 filterповертає ітератор замість list, ви можете використовувати наступне:

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

re - зло в такому простому завданні, друге - найкраще, на мою думку, причина "є ..." методи є найшвидшими для струн.
f0b0s

приклад вашого фільтра обмежений py2k
SilentGhost

2
@ f0b0s-iu9-info: ти її приурочив? на моїй машині (py3k) повторно вдвічі швидше, ніж з фільтром isdigit, генератор з isdigtзнаходиться на півдорозі між ними
SilentGhost

@SilentGhost: Дякую, я використовував IDLE від py2k. Це зараз виправлено.
Жоао Сільва

1
@asmaier Просто використовуйте rдля сирого рядка:re.sub(r"\D+", "", "aas30dsa20")
Mitch McMabers

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

Ще один варіант генератора.


Вбивство .. + 1 Було б ще краще, якби використовувалася
ламда

17

Ви можете використовувати фільтр:

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

На python3.0 ви повинні приєднатися до цього (якось потворно :()

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

лише у py2k, у py3k він повертає генератор
SilentGhost

перетворити, strщоб listпереконатися, що він працює як на py2, так і на py3:''.join(filter(lambda x: x.isdigit(), list("dasdasd2313dsa")))
Luiz C.

13

по лінії відповіді байєра:

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

Ні, це не буде працювати для від'ємних чисел, оскільки -це не цифра.
Олі

12

Ви можете легко зробити це за допомогою Regex

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

На сьогодні найпростіший спосіб
Iorek

5
Чим це відрізняється від відповіді Жоао Сільви, яку було надано на 7 років раніше?
jww

7
x.translate(None, string.digits)

видалить усі цифри з рядка. Щоб видалити літери та зберегти цифри, зробіть це:

x.translate(None, string.letters)

3
Я отримую TypeError: translate () бере рівно один аргумент (2 наведено). Чому це питання було сприйнято в його нинішньому стані, досить неприємно.
Бобборт

перекласти змінено з python 2 на 3. Синтаксис, що використовує цей метод у python 3, є x.translate (str.maketrans ('', '', string.digits)) і x.translate (str.maketrans ('', '' , string.ascii_letters)). Жодна з цих смужок не пропускає простір. Я б дуже не рекомендував такий підхід більше ...
ZaxR

5

У коментарях оп згадується, що він хоче зберегти десяткове місце. Це можна зробити за допомогою методу re.sub (відповідно до другої та найкращої відповіді IMHO), чітко вказавши символи, які слід зберегти, наприклад

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

А як щодо "poo123.4and.5fish"?
Ян Тойнар

У своєму коді я перевіряю кількість періодів у рядку введення та створюю помилку, якщо це більше 1.
Roger Heathcote

4

Швидка версія для Python 3:

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

Ось порівняння продуктивності та регексу:

$ 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

Тож для мене це трохи більше, ніж у 3 рази швидше, ніж регулярний вираз. Це також швидше, ніж class Delвище, тому defaultdictщо всі його пошуки в C, а не (повільний) Python. Ось ця версія в моїй самій системі для порівняння.

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

3

Використовуйте вираз генератора:

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

натомість зробіть''.join(n for n in foo if n.isdigit())
shxfee

2

Некрасиво, але працює:

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

чому ти робиш list(s)?
SilentGhost

@SilentGhost - це моє непорозуміння. було виправлено подяку :)
Gant,

Власне, за допомогою цього методу я не думаю, що вам потрібно використовувати "приєднуватися". filter(lambda x: x.isdigit(), s)добре працював для мене. ... о, це тому, що я використовую Python 2.7.
Бобборт

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

100000 петель, найкраще 3: 2,48 Usec на цикл

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

100000 петель, найкраще 3: 2,02 Usec на петлю

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

100000 петель, найкраще 3: 2,37 Usec на цикл

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

100000 петель, найкраще 3: 1,97 Usec за петлю

Я зауважував, що приєднання швидше, ніж суб.


Чому ви повторюєте ці два способи двічі? А ви могли б описати, чим відрізняється ваша відповідь від прийнятої?
Ян Тойнар

Обидва результати однакові. Але я просто хочу показати, що швидше приєднатись до результатів.
AnilReddy

Вони ні, ваш код робить навпаки. А також у вас є чотири вимірювання, але лише два методи.
Ян Тойнар

1

Ви можете прочитати кожного символу. Якщо вона цифра, то включіть її у відповідь. str.isdigit() Метод є способом дізнатися , чи є символ цифрою.

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

чим це відрізняється від відповіді на f0b0s? Ви повинні замість цього відредагувати, якщо у вас є додаткова інформація
chevybow

0

Не один вкладиш, а дуже простий:

buffer = ""
some_str = "aas30dsa20"

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

print( buffer )

0

Я цим користувався. 'letters'має містити всі листи, від яких ви хочете позбутися:

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

Приклад:

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

Вихід: 20

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.