Удалить символы, кроме цифр из строки, используя 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метод стоит знать и использовать. Другой популярный не-RE подход ...:

$ 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% медленнее, чем RE, поэтому .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 # всегда имели один и тот же мем «строка означает юникод» ... некоторые накладные расходы, может быть, но НАМНОГО лучше поддерживать практически все, кроме английского! -).
Алекс Мартелли
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'
Жоао Силва
источник
Это зло в такой простой задаче, второе, я думаю, лучшее, потому что методы "is ..." самые быстрые для строк.
f0b0s
пример вашего фильтра ограничен py2k
SilentGhost 20.09.09
2
@ f0b0s-iu9-info: вы рассчитывали? на моей машине (py3k) re в два раза быстрее, чем фильтр с isdigit, генератор с isdigtнаходится на полпути между ними
SilentGhost
@SilentGhost: Спасибо, я использовал IDLE из py2k. Это исправлено сейчас.
Жоау Сильва
1
@asmaier Просто используйте rдля сырой строки:re.sub(r"\D+", "", "aas30dsa20")
Митч Макмаберс
64
s=''.join(i for i in s if i.isdigit())

Еще один вариант генератора.

f0b0s
источник
Убил его .. + 1 было бы еще лучше, если бы использовалась
лямда
17

Вы можете использовать фильтр:

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

На python3.0 вы должны присоединиться к этому (довольно некрасиво :()

''.join(filter(lambda x: x.isdigit(), "dasdasd2313dsa"))
freiksenet
источник
только в py2k, в py3k он возвращает генератор
SilentGhost 20.09.09
преобразовать strв, listчтобы убедиться, что он работает как на py2, так и на py3:''.join(filter(lambda x: x.isdigit(), list("dasdasd2313dsa")))
Luiz C.
13

в соответствии с ответом Байера:

''.join(i for i in s if i.isdigit())
SilentGhost
источник
Нет, это не будет работать для отрицательных чисел, потому что -это не цифра.
Оли
12

Вы можете легко сделать это с помощью Regex

>>> import re
>>> re.sub("\D","","£70,000")
70000
Амина Нураини
источник
Безусловно самый простой способ
Йорек
5
Чем это отличается от ответа Жуана Силвы, который был предоставлен 7 годами ранее?
19
7
x.translate(None, string.digits)

удалит все цифры из строки. Чтобы удалить буквы и сохранить цифры, сделайте это:

x.translate(None, string.letters)
Терье Мольнес
источник
3
Я получаю TypeError: translate () принимает ровно один аргумент (2 дано). Почему этот вопрос был поставлен на голосование в его нынешнем состоянии, довольно расстраивает.
Боборт
translate изменен с 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
rescdsk
источник
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'
>>>
Gant
источник
почему ты делаешь list(s)?
SilentGhost 20.09.09
@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 мксек на цикл

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

100000 циклов, лучшее из 3: 2,02 пользователя на цикл

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

100000 циклов, лучше всего 3: 2,37 циклов на цикл

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

100000 циклов, лучшее из 3: 1,97 циклов за цикл

Я заметил, что соединение быстрее, чем саб.

AnilReddy
источник
Почему вы повторяете два метода дважды? И не могли бы вы описать, чем ваш ответ отличается от принятого?
Ян Тойнар
Оба результата одинаковы. Но я просто хочу показать, что объединение - это более быстрый метод в результатах.
AnilReddy
Они не делают, ваш код делает противоположное. А также у вас есть четыре измерения, но только два метода.
Ян Тойнар
1

Вы можете прочитать каждый символ. Если это цифра, то включите ее в ответ. str.isdigit() Метод является способом узнать , является ли символ цифрой.

your_input = '12kjkh2nnk34l34'
your_output = ''.join(c for c in your_input if c.isdigit())
print(your_output) # '1223434'
yoelvis
источник
чем это отличается от ответа 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

Gustav
источник